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
* of an array of int4 is equal to the given value:
*
* array_int4eq({1,2,3}, 1) --> true
* array_int4eq({1,2,3}, 4) --> false
* array_int4eq({1,2,3}, 1) --> true
* array_int4eq({1,2,3}, 4) --> false
*
* 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
@ -19,10 +19,10 @@
* the array_int4_like because there is no like operator for int4.
* It is now possible to write queries which look inside the arrays:
*
* create table t(id int4[], txt text[]);
* select * from t where t.id *= 123;
* select * from t where t.txt *~ '[a-z]';
* select * from t where t.txt[1:3] **~ '[a-z]';
* create table t(id int4[], txt text[]);
* select * from t where t.id *= 123;
* select * from t where t.txt *~ '[a-z]';
* select * from t where t.txt[1:3] **~ '[a-z]';
*
* Copyright (c) 1996, Massimo Dal Zotto <dz@cs.unitn.it>
*/
@ -40,93 +40,116 @@
#include "utils/builtins.h"
#include "utils/elog.h"
static int32
array_iterator(Oid elemtype, Oid proc, int and, ArrayType *array, Datum value)
static int32
array_iterator(Oid elemtype, Oid proc, int and, ArrayType * array, Datum value)
{
HeapTuple typ_tuple;
TypeTupleForm typ_struct;
bool typbyval;
int typlen;
func_ptr proc_fn;
int pronargs;
int nitems, i, result;
int ndim, *dim;
char *p;
HeapTuple typ_tuple;
TypeTupleForm typ_struct;
bool typbyval;
int typlen;
func_ptr proc_fn;
int pronargs;
int nitems,
i,
result;
int ndim,
*dim;
char *p;
/* Sanity checks */
if ((array == (ArrayType *) NULL)
|| (ARR_IS_LO(array) == true)) {
/* elog(NOTICE, "array_iterator: array is null"); */
return (0);
}
ndim = ARR_NDIM(array);
dim = ARR_DIMS(array);
nitems = getNitems(ndim, dim);
if (nitems == 0) {
/* elog(NOTICE, "array_iterator: nitems = 0"); */
return (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) {
/* Sanity checks */
if ((array == (ArrayType *) NULL)
|| (ARR_IS_LO(array) == true))
{
/* elog(NOTICE, "array_iterator: array is null"); */
return (0);
}
ndim = ARR_NDIM(array);
dim = ARR_DIMS(array);
nitems = getNitems(ndim, dim);
if (nitems == 0)
{
/* elog(NOTICE, "array_iterator: nitems = 0"); */
return (0);
}
}
}
if (and && result) {
return (1);
} else {
return (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);
}
}
}
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
array_texteq(ArrayType *array, char* value)
array_texteq(ArrayType * array, char *value)
{
return array_iterator((Oid) 25, /* text */
(Oid) 67, /* texteq */
0, /* logical or */
array, (Datum)value);
return array_iterator((Oid) 25, /* text */
(Oid) 67, /* texteq */
0, /* logical or */
array, (Datum) value);
}
int32
array_all_texteq(ArrayType *array, char* value)
array_all_texteq(ArrayType * array, char *value)
{
return array_iterator((Oid) 25, /* text */
(Oid) 67, /* texteq */
1, /* logical and */
array, (Datum)value);
return array_iterator((Oid) 25, /* text */
(Oid) 67, /* texteq */
1, /* logical and */
array, (Datum) value);
}
int32
array_textregexeq(ArrayType *array, char* value)
array_textregexeq(ArrayType * array, char *value)
{
return array_iterator((Oid) 25, /* text */
(Oid) 81, /* textregexeq */
0, /* logical or */
array, (Datum)value);
return array_iterator((Oid) 25, /* text */
(Oid) 81, /* textregexeq */
0, /* logical or */
array, (Datum) value);
}
int32
array_all_textregexeq(ArrayType *array, char* value)
array_all_textregexeq(ArrayType * array, char *value)
{
return array_iterator((Oid) 25, /* text */
(Oid) 81, /* textregexeq */
1, /* logical and */
array, (Datum)value);
return array_iterator((Oid) 25, /* text */
(Oid) 81, /* textregexeq */
1, /* logical and */
array, (Datum) value);
}
/*
@ -175,39 +198,39 @@ array_all_textregexeq(ArrayType *array, char* value)
*/
int32
array_char16eq(ArrayType *array, char* value)
array_char16eq(ArrayType * array, char *value)
{
return array_iterator((Oid) 20, /* char16 */
(Oid) 490, /* char16eq */
0, /* logical or */
array, (Datum)value);
return array_iterator((Oid) 20, /* char16 */
(Oid) 490, /* char16eq */
0, /* logical or */
array, (Datum) value);
}
int32
array_all_char16eq(ArrayType *array, char* value)
array_all_char16eq(ArrayType * array, char *value)
{
return array_iterator((Oid) 20, /* char16 */
(Oid) 490, /* char16eq */
1, /* logical and */
array, (Datum)value);
return array_iterator((Oid) 20, /* char16 */
(Oid) 490, /* char16eq */
1, /* logical and */
array, (Datum) value);
}
int32
array_char16regexeq(ArrayType *array, char* value)
array_char16regexeq(ArrayType * array, char *value)
{
return array_iterator((Oid) 20, /* char16 */
(Oid) 700, /* char16regexeq */
0, /* logical or */
array, (Datum)value);
return array_iterator((Oid) 20, /* char16 */
(Oid) 700, /* char16regexeq */
0, /* logical or */
array, (Datum) value);
}
int32
array_all_char16regexeq(ArrayType *array, char* value)
array_all_char16regexeq(ArrayType * array, char *value)
{
return array_iterator((Oid) 20, /* char16 */
(Oid) 700, /* char16regexeq */
1, /* logical and */
array, (Datum)value);
return array_iterator((Oid) 20, /* char16 */
(Oid) 700, /* char16regexeq */
1, /* logical and */
array, (Datum) value);
}
/*
@ -215,37 +238,37 @@ array_all_char16regexeq(ArrayType *array, char* value)
*/
int32
array_int4eq(ArrayType *array, int4 value)
array_int4eq(ArrayType * array, int4 value)
{
return array_iterator((Oid) 23, /* int4 */
(Oid) 65, /* int4eq */
0, /* logical or */
array, (Datum)value);
return array_iterator((Oid) 23, /* int4 */
(Oid) 65, /* int4eq */
0, /* logical or */
array, (Datum) value);
}
int32
array_all_int4eq(ArrayType *array, int4 value)
array_all_int4eq(ArrayType * array, int4 value)
{
return array_iterator((Oid) 23, /* int4 */
(Oid) 65, /* int4eq */
1, /* logical and */
array, (Datum)value);
return array_iterator((Oid) 23, /* int4 */
(Oid) 65, /* int4eq */
1, /* logical and */
array, (Datum) value);
}
int32
array_int4gt(ArrayType *array, int4 value)
array_int4gt(ArrayType * array, int4 value)
{
return array_iterator((Oid) 23, /* int4 */
(Oid) 147, /* int4gt */
0, /* logical or */
array, (Datum)value);
return array_iterator((Oid) 23, /* int4 */
(Oid) 147, /* int4gt */
0, /* logical or */
array, (Datum) value);
}
int32
array_all_int4gt(ArrayType *array, int4 value)
array_all_int4gt(ArrayType * array, int4 value)
{
return array_iterator((Oid) 23, /* int4 */
(Oid) 147, /* int4gt */
1, /* logical and */
array, (Datum)value);
return array_iterator((Oid) 23, /* int4 */
(Oid) 147, /* int4gt */
1, /* logical and */
array, (Datum) value);
}

View File

@ -13,86 +13,99 @@
#include "utils/datetime.h"
TimeADT *time_difference(TimeADT * time1, TimeADT * time2)
TimeADT *
time_difference(TimeADT * time1, TimeADT * time2)
{
TimeADT *result = (TimeADT *) palloc(sizeof(TimeADT));
*result = *time1 - *time2;
return (result);
TimeADT *result = (TimeADT *) palloc(sizeof(TimeADT));
*result = *time1 - *time2;
return (result);
}
TimeADT *currenttime()
TimeADT *
currenttime()
{
time_t current_time;
struct tm *tm;
TimeADT *result = (TimeADT *) palloc(sizeof(TimeADT));
time_t current_time;
struct tm *tm;
TimeADT *result = (TimeADT *) palloc(sizeof(TimeADT));
current_time = time(NULL);
tm = localtime(&current_time);
*result = ((((tm->tm_hour*60)+tm->tm_min)*60)+tm->tm_sec);
return (result);
current_time = time(NULL);
tm = localtime(&current_time);
*result = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec);
return (result);
}
DateADT currentdate()
DateADT
currentdate()
{
time_t current_time;
struct tm *tm;
DateADT result;
current_time = time(NULL);
tm = localtime(&current_time);
time_t current_time;
struct tm *tm;
DateADT result;
result = date2j(tm->tm_year,tm->tm_mon + 1,tm->tm_mday) -
date2j(100,1,1);
return (result);
current_time = time(NULL);
tm = localtime(&current_time);
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)),
&tm.tm_year, &tm.tm_mon, &tm.tm_mday);
j2date((*date + date2j(2000, 1, 1)),
&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)),
&tm.tm_year, &tm.tm_mon, &tm.tm_mday);
j2date((*date + date2j(2000, 1, 1)),
&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)),
&tm.tm_year, &tm.tm_mon, &tm.tm_mday);
j2date((*date + date2j(2000, 1, 1)),
&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--
* Internal 64-bit integer operations
* Internal 64-bit integer operations
*
*-------------------------------------------------------------------------
*/
#include <stdio.h> /* for sprintf proto, etc. */
#include <stdlib.h> /* for strtod, etc. */
#include <stdio.h> /* for sprintf proto, etc. */
#include <stdlib.h> /* for strtod, etc. */
#include <string.h>
#include <ctype.h>
#include <time.h>
@ -17,7 +17,7 @@
#include "postgres.h"
#include "utils/palloc.h"
#define MAXINT8LEN 25
#define MAXINT8LEN 25
#define USE_LOCAL_CODE 1
@ -26,53 +26,58 @@
#endif
#ifndef HAVE_64BIT_INTS
typedef char[8] int64;
typedef char [8] int64;
#elif defined(__alpha)
typedef long int int64;
#define INT64_FORMAT "%ld"
#elif defined(__GNUC__)
typedef long long int int64;
#define INT64_FORMAT "%Ld"
#else
typedef long int int64;
#define INT64_FORMAT "%ld"
#endif
int64 *int8in(char *str);
char *int8out(int64 *val);
int64 *int8in(char *str);
char *int8out(int64 * val);
bool int8eq(int64 *val1, int64 *val2);
bool int8ne(int64 *val1, int64 *val2);
bool int8lt(int64 *val1, int64 *val2);
bool int8gt(int64 *val1, int64 *val2);
bool int8le(int64 *val1, int64 *val2);
bool int8ge(int64 *val1, int64 *val2);
bool int8eq(int64 * val1, int64 * val2);
bool int8ne(int64 * val1, int64 * val2);
bool int8lt(int64 * val1, int64 * val2);
bool int8gt(int64 * val1, int64 * val2);
bool int8le(int64 * val1, int64 * val2);
bool int8ge(int64 * val1, int64 * val2);
bool int84eq(int64 *val1, int32 val2);
bool int84ne(int64 *val1, int32 val2);
bool int84lt(int64 *val1, int32 val2);
bool int84gt(int64 *val1, int32 val2);
bool int84le(int64 *val1, int32 val2);
bool int84ge(int64 *val1, int32 val2);
bool int84eq(int64 * val1, int32 val2);
bool int84ne(int64 * val1, int32 val2);
bool int84lt(int64 * val1, int32 val2);
bool int84gt(int64 * val1, int32 val2);
bool int84le(int64 * val1, int32 val2);
bool int84ge(int64 * val1, int32 val2);
int64 *int8um(int64 *val);
int64 *int8pl(int64 *val1, int64 *val2);
int64 *int8mi(int64 *val1, int64 *val2);
int64 *int8mul(int64 *val1, int64 *val2);
int64 *int8div(int64 *val1, int64 *val2);
int64 *int8um(int64 * val);
int64 *int8pl(int64 * val1, int64 * val2);
int64 *int8mi(int64 * val1, int64 * val2);
int64 *int8mul(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
int64 *int28(int16 val);
int16 int82(int64 *val);
int64 *int28(int16 val);
int16 int82(int64 * val);
#endif
float64 i8tod(int64 *val);
int64 *dtoi8(float64 val);
float64 i8tod(int64 * val);
int64 *dtoi8(float64 val);
#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()
*/
int64 *int8in(char *str)
int64 *
int8in(char *str)
{
int64 *result = PALLOCTYPE(int64);
int64 *result = PALLOCTYPE(int64);
#if HAVE_64BIT_INTS
if (!PointerIsValid(str))
elog (WARN,"Bad (null) int8 external representation",NULL);
if (!PointerIsValid(str))
elog(WARN, "Bad (null) int8 external representation", NULL);
if (sscanf(str, INT64_FORMAT, result) != 1)
elog(WARN,"Bad int8 external representation '%s'",str);
if (sscanf(str, INT64_FORMAT, result) != 1)
elog(WARN, "Bad int8 external representation '%s'", str);
#else
elog(WARN,"64-bit integers are not supported",NULL);
result = NULL;
elog(WARN, "64-bit integers are not supported", NULL);
result = NULL;
#endif
return(result);
} /* int8in() */
return (result);
} /* int8in() */
/* int8out()
*/
char *int8out(int64 *val)
char *
int8out(int64 * val)
{
char *result;
char *result;
int len;
char buf[MAXINT8LEN+1];
int len;
char buf[MAXINT8LEN + 1];
#if HAVE_64BIT_INTS
if (!PointerIsValid(val))
return(NULL);
if (!PointerIsValid(val))
return (NULL);
if ((len = snprintf( buf, MAXINT8LEN, INT64_FORMAT, *val)) < 0)
elog (WARN,"Unable to format int8",NULL);
if ((len = snprintf(buf, MAXINT8LEN, INT64_FORMAT, *val)) < 0)
elog(WARN, "Unable to format int8", NULL);
result = PALLOC(len+1);
result = PALLOC(len + 1);
strcpy(result, buf);
strcpy(result, buf);
#else
elog(WARN,"64-bit integers are not supported",NULL);
result = NULL;
elog(WARN, "64-bit integers are not supported", NULL);
result = NULL;
#endif
return( result);
} /* int8out() */
return (result);
} /* int8out() */
/*----------------------------------------------------------
* Relational operators for int8s.
* Relational operators for int8s.
*---------------------------------------------------------*/
/* int8relop()
* Is val1 relop val2?
*/
bool int8eq(int64 *val1, int64 *val2)
bool
int8eq(int64 * val1, int64 * val2)
{
return(*val1 == *val2);
} /* int8eq() */
return (*val1 == *val2);
} /* int8eq() */
bool int8ne(int64 *val1, int64 *val2)
bool
int8ne(int64 * val1, int64 * val2)
{
return(*val1 != *val2);
} /* int8ne() */
return (*val1 != *val2);
} /* int8ne() */
bool int8lt(int64 *val1, int64 *val2)
bool
int8lt(int64 * val1, int64 * val2)
{
return(*val1 < *val2);
} /* int8lt() */
return (*val1 < *val2);
} /* int8lt() */
bool int8gt(int64 *val1, int64 *val2)
bool
int8gt(int64 * val1, int64 * val2)
{
return(*val1 > *val2);
} /* int8gt() */
return (*val1 > *val2);
} /* int8gt() */
bool int8le(int64 *val1, int64 *val2)
bool
int8le(int64 * val1, int64 * val2)
{
return(*val1 <= *val2);
} /* int8le() */
return (*val1 <= *val2);
} /* int8le() */
bool int8ge(int64 *val1, int64 *val2)
bool
int8ge(int64 * val1, int64 * val2)
{
return(*val1 >= *val2);
} /* int8ge() */
return (*val1 >= *val2);
} /* int8ge() */
/* int84relop()
* Is 64-bit val1 relop 32-bit val2?
*/
bool int84eq(int64 *val1, int32 val2)
bool
int84eq(int64 * val1, int32 val2)
{
return(*val1 == val2);
} /* int84eq() */
return (*val1 == val2);
} /* int84eq() */
bool int84ne(int64 *val1, int32 val2)
bool
int84ne(int64 * val1, int32 val2)
{
return(*val1 != val2);
} /* int84ne() */
return (*val1 != val2);
} /* int84ne() */
bool int84lt(int64 *val1, int32 val2)
bool
int84lt(int64 * val1, int32 val2)
{
return(*val1 < val2);
} /* int84lt() */
return (*val1 < val2);
} /* int84lt() */
bool int84gt(int64 *val1, int32 val2)
bool
int84gt(int64 * val1, int32 val2)
{
return(*val1 > val2);
} /* int84gt() */
return (*val1 > val2);
} /* int84gt() */
bool int84le(int64 *val1, int32 val2)
bool
int84le(int64 * val1, int32 val2)
{
return(*val1 <= val2);
} /* int84le() */
return (*val1 <= val2);
} /* int84le() */
bool int84ge(int64 *val1, int32 val2)
bool
int84ge(int64 * val1, int32 val2)
{
return(*val1 >= val2);
} /* int84ge() */
return (*val1 >= val2);
} /* 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))
return NULL;
if (!PointerIsValid(val))
return NULL;
*result = (- *val);
*result = (-*val);
return(result);
} /* int8um() */
return (result);
} /* 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)))
return NULL;
if ((!PointerIsValid(val1)) || (!PointerIsValid(val2)))
return NULL;
*result = *val1 + *val2;
*result = *val1 + *val2;
return(result);
} /* int8pl() */
return (result);
} /* 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)))
return NULL;
if ((!PointerIsValid(val1)) || (!PointerIsValid(val2)))
return NULL;
*result = *val1 - *val2;
*result = *val1 - *val2;
return(result);
} /* int8mi() */
return (result);
} /* 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)))
return NULL;
if ((!PointerIsValid(val1)) || (!PointerIsValid(val2)))
return NULL;
*result = *val1 * *val2;
*result = *val1 * *val2;
return(result);
} /* int8mul() */
return (result);
} /* 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)))
return NULL;
if ((!PointerIsValid(val1)) || (!PointerIsValid(val2)))
return NULL;
*result = *val1 / *val2;
*result = *val1 / *val2;
return(result);
} /* int8div() */
return (result);
} /* 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);
} /* int48() */
return (result);
} /* int48() */
int32 int84(int64 *val)
int32
int84(int64 * val)
{
int32 result;
int32 result;
if (!PointerIsValid(val))
elog(WARN,"Invalid (null) int64, can't convert int8 to int4",NULL);
if (!PointerIsValid(val))
elog(WARN, "Invalid (null) int64, can't convert int8 to int4", NULL);
if ((*val < INT_MIN) || (*val > INT_MAX))
elog(WARN,"int8 conversion to int4 is out of range",NULL);
if ((*val < INT_MIN) || (*val > INT_MAX))
elog(WARN, "int8 conversion to int4 is out of range", NULL);
result = *val;
result = *val;
return(result);
} /* int84() */
return (result);
} /* int84() */
#if FALSE
int64 *int28(int16 val)
int64 *
int28(int16 val)
{
int64 *result;
int64 *result;
if (!PointerIsValid(result = PALLOCTYPE(int64)))
elog(WARN,"Memory allocation failed, can't convert int8 to int2",NULL);
if (!PointerIsValid(result = PALLOCTYPE(int64)))
elog(WARN, "Memory allocation failed, can't convert int8 to int2", NULL);
*result = val;
*result = val;
return(result);
} /* int28() */
return (result);
} /* int28() */
int16 int82(int64 *val)
int16
int82(int64 * val)
{
int16 result;
int16 result;
if (!PointerIsValid(val))
elog(WARN,"Invalid (null) int8, can't convert to int2",NULL);
if (!PointerIsValid(val))
elog(WARN, "Invalid (null) int8, can't convert to int2", NULL);
result = *val;
result = *val;
return (result);
} /* int82() */
return(result);
} /* int82() */
#endif
float64 i8tod(int64 *val)
float64
i8tod(int64 * val)
{
float64 result = PALLOCTYPE(float64data);
float64 result = PALLOCTYPE(float64data);
*result = *val;
*result = *val;
return(result);
} /* i8tod() */
return (result);
} /* 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)))
elog(WARN,"Floating point conversion to int64 is out of range",NULL);
if ((*val < (-pow(2, 64) + 1)) || (*val > (pow(2, 64) - 1)))
elog(WARN, "Floating point conversion to int64 is out of range", NULL);
*result = *val;
return(result);
} /* dtoi8() */
*result = *val;
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>
@ -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*/
void halt(va_alist)
void
halt(va_alist)
va_dcl
{
va_list arg_ptr;
char *format, *pstr;
void (*sig_func)();
va_list arg_ptr;
char *format,
*pstr;
void (*sig_func) ();
va_start(arg_ptr);
format = va_arg(arg_ptr,char *);
if (strncmp(format,"PERROR", 6) != 0)
vfprintf(stderr,format,arg_ptr);
format = va_arg(arg_ptr, char *);
if (strncmp(format, "PERROR", 6) != 0)
vfprintf(stderr, format, arg_ptr);
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("");
}
}
va_end(arg_ptr);
fflush(stderr);
/* call one clean up function if defined */
if ( (sig_func = signal(SIGTERM, SIG_DFL)) != SIG_DFL &&
sig_func != SIG_IGN)
(*sig_func)(0);
else if ( (sig_func = signal(SIGHUP, SIG_DFL)) != SIG_DFL &&
sig_func != SIG_IGN)
(*sig_func)(0);
else if ( (sig_func = signal(SIGINT, SIG_DFL)) != SIG_DFL &&
sig_func != SIG_IGN)
(*sig_func)(0);
else if ( (sig_func = signal(SIGQUIT, SIG_DFL)) != SIG_DFL &&
sig_func != SIG_IGN)
(*sig_func)(0);
/* call one clean up function if defined */
if ((sig_func = signal(SIGTERM, SIG_DFL)) != SIG_DFL &&
sig_func != SIG_IGN)
(*sig_func) (0);
else if ((sig_func = signal(SIGHUP, SIG_DFL)) != SIG_DFL &&
sig_func != SIG_IGN)
(*sig_func) (0);
else if ((sig_func = signal(SIGINT, SIG_DFL)) != SIG_DFL &&
sig_func != SIG_IGN)
(*sig_func) (0);
else if ((sig_func = signal(SIGQUIT, SIG_DFL)) != SIG_DFL &&
sig_func != SIG_IGN)
(*sig_func) (0);
exit(1);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -17,14 +17,14 @@
/* define this if you want to see iso-8859 characters */
#define ISO8859
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define VALUE(char) ((char) - '0')
#define DIGIT(val) ((val) + '0')
#define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define VALUE(char) ((char) - '0')
#define DIGIT(val) ((val) + '0')
#define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
#ifndef ISO8859
#define NOTPRINTABLE(c) (!isprint(c))
#define NOTPRINTABLE(c) (!isprint(c))
#else
#define NOTPRINTABLE(c) (!isprint(c) && ((c) < 0xa0))
#define NOTPRINTABLE(c) (!isprint(c) && ((c) < 0xa0))
#endif
/*
@ -36,115 +36,129 @@
* The function is used by output methods of various string types.
*
* Arguments:
* data - input data (can be NULL)
* size - optional size of data. A negative value indicates
* that data is a null terminated string.
* data - input data (can be NULL)
* size - optional size of data. A negative value indicates
* that data is a null terminated string.
*
* Returns:
* a pointer to a new string containing the printable
* representation of data.
* a pointer to a new string containing the printable
* representation of data.
*/
char *
char *
string_output(char *data, int size)
{
register unsigned char c, *p, *r, *result;
register int l, len;
register unsigned char c,
*p,
*r,
*result;
register int l,
len;
if (data == NULL) {
result = (char *) palloc(2);
result[0] = '-';
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;
}
if (data == NULL)
{
result = (char *) palloc(2);
result[0] = '-';
result[1] = '\0';
return (result);
}
}
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;
}
if (size < 0)
{
size = strlen(data);
}
}
*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);
}
/*
* string_input() --
*
* This function accepts a C string in input and copies it into a new
* This function accepts a C string in input and copies it into a new
* object allocated with palloc() translating all escape sequences.
* An optional header can be allocatd before the string, for example
* to hold the length of a varlena object.
@ -153,209 +167,231 @@ string_output(char *data, int size)
* receive strings in internal form.
*
* Arguments:
* str - input string possibly with escapes
* size - the required size of new data. A value of 0
* indicates a variable size string, while a
* negative value indicates a variable size string
* of size not greater than this absolute value.
* hdrsize - size of an optional header to be allocated before
* the data. It must then be filled by the caller.
* rtn_size - an optional pointer to an int variable where the
* size of the new string is stored back.
* str - input string possibly with escapes
* size - the required size of new data. A value of 0
* indicates a variable size string, while a
* negative value indicates a variable size string
* of size not greater than this absolute value.
* hdrsize - size of an optional header to be allocated before
* the data. It must then be filled by the caller.
* rtn_size - an optional pointer to an int variable where the
* size of the new string is stored back.
*
* 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)
{
register unsigned char *p, *r;
unsigned char *result;
int len;
register unsigned char *p,
*r;
unsigned char *result;
int len;
if ((str == NULL) || (hdrsize < 0)) {
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--;
if ((str == NULL) || (hdrsize < 0))
{
return (char *) NULL;
}
}
/* 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++);
/* 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--;
}
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)
{
char str[2];
char str[2];
str[0] = (char) c;
str[1] = '\0';
str[0] = (char) c;
str[1] = '\0';
return (string_output(str, 1));
return (string_output(str, 1));
}
char *
char *
c_char2out(uint16 s)
{
return (string_output((char *) &s, 2));
return (string_output((char *) &s, 2));
}
char *
char *
c_char4out(uint32 s)
{
return (string_output((char *) &s, 4));
return (string_output((char *) &s, 4));
}
char *
char *
c_char8out(char *s)
{
return (string_output(s, 8));
return (string_output(s, 8));
}
char *
char *
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
*/
char *
c_textout(struct varlena *vlena)
char *
c_textout(struct varlena * vlena)
{
int len = 0;
char *s = NULL;
int len = 0;
char *s = NULL;
if (vlena) {
len = VARSIZE(vlena) - VARHDRSZ;
s = VARDATA(vlena);
}
return (string_output(s, len));
if (vlena)
{
len = VARSIZE(vlena) - VARHDRSZ;
s = VARDATA(vlena);
}
return (string_output(s, len));
}
/*
* This can be used for varchar and bpchar strings
*/
char *
char *
c_varcharout(char *s)
{
int len;
int len;
if (s) {
len = *(int32*)s - 4;
s += 4;
}
return (string_output(s, len));
if (s)
{
len = *(int32 *) s - 4;
s += 4;
}
return (string_output(s, len));
}
#ifdef 0
struct varlena *
c_textin(char *str)
{
struct varlena *result;
int len;
struct varlena *result;
int len;
if (str == NULL) {
return ((struct varlena *) NULL);
}
if (str == NULL)
{
return ((struct varlena *) NULL);
}
result = (struct varlena *) string_input(str, 0, VARHDRSZ, &len);
VARSIZE(result) = len;
result = (struct varlena *) string_input(str, 0, VARHDRSZ, &len);
VARSIZE(result) = len;
return (result);
return (result);
}
char *
char *
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--
* heap tuple qualification validity checking code
* heap tuple qualification validity checking code
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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>
/* ----------------
* 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
heap_keytest(HeapTuple t,
TupleDesc tupdesc,
int nkeys,
ScanKey keys)
TupleDesc tupdesc,
int nkeys,
ScanKey keys)
{
bool isnull;
Datum atp;
int test;
for (; nkeys--; keys++) {
atp = (Datum)heap_getattr(t, InvalidBuffer,
keys->sk_attno,
tupdesc,
&isnull);
if (isnull)
/* XXX eventually should check if SK_ISNULL */
return false;
if (keys->sk_flags & SK_ISNULL) {
return (false);
bool isnull;
Datum atp;
int test;
for (; nkeys--; keys++)
{
atp = (Datum) heap_getattr(t, InvalidBuffer,
keys->sk_attno,
tupdesc,
&isnull);
if (isnull)
/* XXX eventually should check if SK_ISNULL */
return false;
if (keys->sk_flags & SK_ISNULL)
{
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 */
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;
return true;
}
/* ----------------
* heap_tuple_satisfies
* heap_tuple_satisfies
*
* Returns a valid HeapTuple if it satisfies the timequal and keytest.
* 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
* PageGetItem's per tuple.
* Returns a valid HeapTuple if it satisfies the timequal and keytest.
* 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
* PageGetItem's per tuple.
*
* Complete check of validity including LP_CTUP and keytest.
* This should perhaps be combined with valid somehow in the
* future. (Also, additional rule tests/time range tests.)
* Complete check of validity including LP_CTUP and keytest.
* This should perhaps be combined with valid somehow in the
* future. (Also, additional rule tests/time range tests.)
*
* on 8/21/92 mao says: i rearranged the tests here to do keytest before
* SatisfiesTimeQual. profiling indicated that even for vacuumed relations,
* 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
* the restriction and optimize it in the normal way. this has interactions
* with joey's expensive function work.
* on 8/21/92 mao says: i rearranged the tests here to do keytest before
* SatisfiesTimeQual. profiling indicated that even for vacuumed relations,
* 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
* the restriction and optimize it in the normal way. this has interactions
* with joey's expensive function work.
* ----------------
*/
HeapTuple
heap_tuple_satisfies(ItemId itemId,
Relation relation,
Buffer buffer,
PageHeader disk_page,
TimeQual qual,
int nKeys,
ScanKey key)
Relation relation,
Buffer buffer,
PageHeader disk_page,
TimeQual qual,
int nKeys,
ScanKey key)
{
HeapTuple tuple, result;
bool res;
TransactionId old_tmin, old_tmax;
HeapTuple tuple,
result;
bool res;
TransactionId old_tmin,
old_tmax;
if (! ItemIdIsUsed(itemId))
return NULL;
tuple = (HeapTuple) PageGetItem((Page) disk_page, itemId);
if (key != NULL)
res = heap_keytest(tuple, RelationGetTupleDescriptor(relation),
nKeys, key);
else
res = TRUE;
if (!ItemIdIsUsed(itemId))
return NULL;
result = (HeapTuple)NULL;
if (res) {
if(relation->rd_rel->relkind == RELKIND_UNCATALOGED) {
result = tuple;
} else {
old_tmin = tuple->t_tmin;
old_tmax = tuple->t_tmax;
res = HeapTupleSatisfiesTimeQual(tuple,qual);
if(tuple->t_tmin != old_tmin ||
tuple->t_tmax != old_tmax) {
SetBufferCommitInfoNeedsSave(buffer);
}
if(res) {
result = tuple;
}
tuple = (HeapTuple) PageGetItem((Page) disk_page, itemId);
if (key != NULL)
res = heap_keytest(tuple, RelationGetTupleDescriptor(relation),
nKeys, key);
else
res = TRUE;
result = (HeapTuple) NULL;
if (res)
{
if (relation->rd_rel->relkind == RELKIND_UNCATALOGED)
{
result = tuple;
}
else
{
old_tmin = tuple->t_tmin;
old_tmax = tuple->t_tmax;
res = HeapTupleSatisfiesTimeQual(tuple, qual);
if (tuple->t_tmin != old_tmin ||
tuple->t_tmax != old_tmax)
{
SetBufferCommitInfoNeedsSave(buffer);
}
if (res)
{
result = tuple;
}
}
}
}
return result;
return result;
}
/*
* TupleUpdatedByCurXactAndCmd() -- Returns true if this tuple has
* already been updated once by the current transaction/command
* pair.
* TupleUpdatedByCurXactAndCmd() -- Returns true if this tuple has
* already been updated once by the current transaction/command
* pair.
*/
bool
TupleUpdatedByCurXactAndCmd(HeapTuple t)
{
if (TransactionIdEquals(t->t_xmax,
GetCurrentTransactionId()) &&
CommandIdGEScanCommandId (t->t_cmax))
return true;
return false;
if (TransactionIdEquals(t->t_xmax,
GetCurrentTransactionId()) &&
CommandIdGEScanCommandId(t->t_cmax))
return true;
return false;
}

View File

@ -1,14 +1,14 @@
/*-------------------------------------------------------------------------
*
* indextuple.c--
* This file contains index tuple accessor and mutator routines,
* as well as a few various tuple utilities.
* This file contains index tuple accessor and mutator routines,
* as well as a few various tuple utilities.
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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>
#ifndef HAVE_MEMMOVE
# include <regex/utils.h>
#include <regex/utils.h>
#else
# include <string.h>
#include <string.h>
#endif
static Size IndexInfoFindDataOffset(unsigned short t_info);
static char *fastgetiattr(IndexTuple tup, int attnum,
TupleDesc att, bool *isnull);
static Size IndexInfoFindDataOffset(unsigned short t_info);
static char *
fastgetiattr(IndexTuple tup, int attnum,
TupleDesc att, bool * isnull);
/* ----------------------------------------------------------------
* index_ tuple interface routines
* index_ tuple interface routines
* ----------------------------------------------------------------
*/
/* ----------------
* index_formtuple
* index_formtuple
* ----------------
*/
IndexTuple
index_formtuple(TupleDesc tupleDescriptor,
Datum value[],
char null[])
Datum value[],
char null[])
{
register char *tp; /* tuple pointer */
IndexTuple tuple; /* return tuple */
Size size, hoff;
int i;
unsigned short infomask = 0;
bool hasnull = false;
char tupmask = 0;
int numberOfAttributes = tupleDescriptor->natts;
if (numberOfAttributes > MaxIndexAttributeNumber)
elog(WARN, "index_formtuple: numberOfAttributes of %d > %d",
numberOfAttributes, MaxIndexAttributeNumber);
for (i = 0; i < numberOfAttributes && !hasnull; i++) {
if (null[i] != ' ') hasnull = true;
}
if (hasnull) infomask |= INDEX_NULL_MASK;
hoff = IndexInfoFindDataOffset(infomask);
size = hoff
+ ComputeDataSize(tupleDescriptor,
value, null);
size = DOUBLEALIGN(size); /* be conservative */
tp = (char *) palloc(size);
tuple = (IndexTuple) tp;
memset(tp,0,(int)size);
DataFill((char *)tp + hoff,
tupleDescriptor,
value,
null,
&tupmask,
(hasnull ? (bits8*)tp + sizeof(*tuple) : NULL));
/*
* We do this because DataFill wants to initialize a "tupmask" which
* is used for HeapTuples, but we want an indextuple infomask. The only
* "relevent" info is the "has variable attributes" field, which is in
* mask position 0x02. We have already set the null mask above.
*/
if (tupmask & 0x02) infomask |= INDEX_VAR_MASK;
/*
* 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
* rather odd way to make sure the size is not too large overall.
*/
if (size & 0xE000)
elog(WARN, "index_formtuple: data takes %d bytes: too big", size);
register char *tp; /* tuple pointer */
IndexTuple tuple; /* return tuple */
Size size,
hoff;
int i;
unsigned short infomask = 0;
bool hasnull = false;
char tupmask = 0;
int numberOfAttributes = tupleDescriptor->natts;
infomask |= size;
/* ----------------
* initialize metadata
* ----------------
*/
tuple->t_info = infomask;
return (tuple);
if (numberOfAttributes > MaxIndexAttributeNumber)
elog(WARN, "index_formtuple: numberOfAttributes of %d > %d",
numberOfAttributes, MaxIndexAttributeNumber);
for (i = 0; i < numberOfAttributes && !hasnull; i++)
{
if (null[i] != ' ')
hasnull = true;
}
if (hasnull)
infomask |= INDEX_NULL_MASK;
hoff = IndexInfoFindDataOffset(infomask);
size = hoff
+ ComputeDataSize(tupleDescriptor,
value, null);
size = DOUBLEALIGN(size); /* be conservative */
tp = (char *) palloc(size);
tuple = (IndexTuple) tp;
memset(tp, 0, (int) size);
DataFill((char *) tp + hoff,
tupleDescriptor,
value,
null,
&tupmask,
(hasnull ? (bits8 *) tp + sizeof(*tuple) : NULL));
/*
* We do this because DataFill wants to initialize a "tupmask" which
* is used for HeapTuples, but we want an indextuple infomask. The
* only "relevent" info is the "has variable attributes" field, which
* is in mask position 0x02. We have already set the null mask above.
*/
if (tupmask & 0x02)
infomask |= INDEX_VAR_MASK;
/*
* 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
* rather odd way to make sure the size is not too large overall.
*/
if (size & 0xE000)
elog(WARN, "index_formtuple: data takes %d bytes: too big", size);
infomask |= size;
/* ----------------
* initialize metadata
* ----------------
*/
tuple->t_info = infomask;
return (tuple);
}
/* ----------------
* fastgetiattr
* fastgetiattr
*
* This is a newer version of fastgetiattr which attempts to be
* faster by caching attribute offsets in the attribute descriptor.
* This is a newer version of fastgetiattr which attempts to be
* faster by caching attribute offsets in the attribute descriptor.
*
* an alternate way to speed things up would be to cache offsets
* with the tuple, but that seems more difficult unless you take
* the storage hit of actually putting those offsets into the
* tuple you send to disk. Yuck.
* an alternate way to speed things up would be to cache offsets
* with the tuple, but that seems more difficult unless you take
* the storage hit of actually putting those offsets into the
* tuple you send to disk. Yuck.
*
* This scheme will be slightly slower than that, but should
* preform well for queries which hit large #'s of tuples. After
* you cache the offsets once, examining all the other tuples using
* the same attribute descriptor will go much quicker. -cim 5/4/91
* This scheme will be slightly slower than that, but should
* preform well for queries which hit large #'s of tuples. After
* you cache the offsets once, examining all the other tuples using
* the same attribute descriptor will go much quicker. -cim 5/4/91
* ----------------
*/
static char *
static char *
fastgetiattr(IndexTuple tup,
int attnum,
TupleDesc tupleDesc,
bool *isnull)
int attnum,
TupleDesc tupleDesc,
bool * isnull)
{
register char *tp; /* ptr to att in tuple */
register char *bp = NULL; /* ptr to att in tuple */
int slow; /* do we have to walk nulls? */
register int data_off; /* tuple data offset */
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;
register char *tp; /* ptr to att in tuple */
register char *bp = NULL; /* ptr to att in tuple */
int slow; /* do we have to walk nulls? */
register int data_off; /* tuple data offset */
AttributeTupleForm *att = tupleDesc->attrs;
/* ----------------
* check to see if desired att is null
* sanity checks
* ----------------
*/
attnum--;
{
if (att_isnull(attnum, bp)) {
*isnull = true;
return NULL;
}
}
Assert(PointerIsValid(isnull));
Assert(attnum > 0);
/* ----------------
* 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;
finalbit = attnum & 0x07;
for (; i <= byte; i++) {
n = bp[i];
if (i < byte) {
/* check for nulls in any "earlier" bytes */
if ((~n) != 0) {
slow++;
/* 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
* ----------------
*/
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;
}
} 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;
break;
case sizeof(short):
off = SHORTALIGN(off);
break;
off = SHORTALIGN(off);
break;
case sizeof(int32):
off = INTALIGN(off);
break;
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;
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;
}
att[j]->attcacheoff = off;
off += att[j]->attlen;
return (fetchatt(&att[attnum], tp + off));
}
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
index_getattr(IndexTuple tuple,
AttrNumber attNum,
TupleDesc tupDesc,
bool *isNullOutP)
AttrNumber attNum,
TupleDesc tupDesc,
bool * isNullOutP)
{
Assert (attNum > 0);
Assert(attNum > 0);
return (Datum)
fastgetiattr(tuple, attNum, tupDesc, isNullOutP);
return (Datum)
fastgetiattr(tuple, attNum, tupDesc, isNullOutP);
}
RetrieveIndexResult
FormRetrieveIndexResult(ItemPointer indexItemPointer,
ItemPointer heapItemPointer)
ItemPointer heapItemPointer)
{
RetrieveIndexResult result;
Assert(ItemPointerIsValid(indexItemPointer));
Assert(ItemPointerIsValid(heapItemPointer));
result = (RetrieveIndexResult) palloc(sizeof *result);
result->index_iptr = *indexItemPointer;
result->heap_iptr = *heapItemPointer;
return (result);
RetrieveIndexResult result;
Assert(ItemPointerIsValid(indexItemPointer));
Assert(ItemPointerIsValid(heapItemPointer));
result = (RetrieveIndexResult) palloc(sizeof *result);
result->index_iptr = *indexItemPointer;
result->heap_iptr = *heapItemPointer;
return (result);
}
/*
@ -425,19 +461,21 @@ FormRetrieveIndexResult(ItemPointer indexItemPointer,
*
* Change me if adding an attribute to IndexTuples!!!!!!!!!!!
*/
static Size
static Size
IndexInfoFindDataOffset(unsigned short t_info)
{
if (!(t_info & INDEX_NULL_MASK))
return((Size) sizeof(IndexTupleData));
else {
Size size = sizeof(IndexTupleData);
if (t_info & INDEX_NULL_MASK) {
size += sizeof(IndexAttributeBitMapData);
if (!(t_info & INDEX_NULL_MASK))
return ((Size) sizeof(IndexTupleData));
else
{
Size size = sizeof(IndexTupleData);
if (t_info & INDEX_NULL_MASK)
{
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.
*/
void
CopyIndexTuple(IndexTuple source, IndexTuple *target)
CopyIndexTuple(IndexTuple source, IndexTuple * target)
{
Size size;
IndexTuple ret;
size = IndexTupleSize(source);
if (*target == NULL) {
*target = (IndexTuple) palloc(size);
}
ret = *target;
memmove((char*)ret, (char*)source, size);
}
Size size;
IndexTuple ret;
size = IndexTupleSize(source);
if (*target == NULL)
{
*target = (IndexTuple) palloc(size);
}
ret = *target;
memmove((char *) ret, (char *) source, size);
}

View File

@ -1,13 +1,13 @@
/*-------------------------------------------------------------------------
*
* indexvalid.c--
* index tuple qualification validity checking code
* index tuple qualification validity checking code
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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>
/* ----------------------------------------------------------------
* index scan key qualification code
* index scan key qualification code
* ----------------------------------------------------------------
*/
int NIndexTupleProcessed;
int NIndexTupleProcessed;
/* ----------------
* index_keytest
* index_keytest
*
* old comments
* May eventually combine with other tests (like timeranges)?
* Should have Buffer buffer; as an argument and pass it to amgetattr.
* May eventually combine with other tests (like timeranges)?
* Should have Buffer buffer; as an argument and pass it to amgetattr.
* ----------------
*/
bool
index_keytest(IndexTuple tuple,
TupleDesc tupdesc,
int scanKeySize,
ScanKey key)
TupleDesc tupdesc,
int scanKeySize,
ScanKey key)
{
bool isNull;
Datum datum;
int test;
IncrIndexProcessed();
while (scanKeySize > 0) {
datum = index_getattr(tuple,
key[0].sk_attno,
tupdesc,
&isNull);
if (isNull) {
/* XXX eventually should check if SK_ISNULL */
return (false);
}
if (key[0].sk_flags & SK_ISNULL) {
return (false);
bool isNull;
Datum datum;
int test;
IncrIndexProcessed();
while (scanKeySize > 0)
{
datum = index_getattr(tuple,
key[0].sk_attno,
tupdesc,
&isNull);
if (isNull)
{
/* 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_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);
return (true);
}

View File

@ -1,14 +1,14 @@
/*-------------------------------------------------------------------------
*
* printtup.c--
* Routines to print out tuples to the destination (binary or non-binary
* portals, frontend/interactive backend, etc.).
* Routines to print out tuples to the destination (binary or non-binary
* portals, frontend/interactive backend, etc.).
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
@ -16,279 +16,304 @@
#include <string.h>
#include <postgres.h>
#include <fmgr.h>
#include <access/heapam.h>
#include <access/printtup.h>
#include <fmgr.h>
#include <access/heapam.h>
#include <access/printtup.h>
#include <catalog/pg_type.h>
#include <libpq/libpq.h>
#include <utils/syscache.h>
/* ----------------------------------------------------------------
* printtup / debugtup support
* printtup / debugtup support
* ----------------------------------------------------------------
*/
/* ----------------
* typtoout - used by printtup and debugtup
* typtoout - used by printtup and debugtup
* ----------------
*/
Oid
typtoout(Oid type)
{
HeapTuple typeTuple;
typeTuple = SearchSysCacheTuple(TYPOID,
ObjectIdGetDatum(type),
0, 0, 0);
if (HeapTupleIsValid(typeTuple))
return((Oid)
((TypeTupleForm) GETSTRUCT(typeTuple))->typoutput);
elog(WARN, "typtoout: Cache lookup of type %d failed", type);
return(InvalidOid);
HeapTuple typeTuple;
typeTuple = SearchSysCacheTuple(TYPOID,
ObjectIdGetDatum(type),
0, 0, 0);
if (HeapTupleIsValid(typeTuple))
return ((Oid)
((TypeTupleForm) GETSTRUCT(typeTuple))->typoutput);
elog(WARN, "typtoout: Cache lookup of type %d failed", type);
return (InvalidOid);
}
Oid
gettypelem(Oid type)
{
HeapTuple typeTuple;
typeTuple = SearchSysCacheTuple(TYPOID,
ObjectIdGetDatum(type),
0,0,0);
if (HeapTupleIsValid(typeTuple))
return((Oid)
((TypeTupleForm) GETSTRUCT(typeTuple))->typelem);
elog(WARN, "typtoout: Cache lookup of type %d failed", type);
return(InvalidOid);
HeapTuple typeTuple;
typeTuple = SearchSysCacheTuple(TYPOID,
ObjectIdGetDatum(type),
0, 0, 0);
if (HeapTupleIsValid(typeTuple))
return ((Oid)
((TypeTupleForm) GETSTRUCT(typeTuple))->typelem);
elog(WARN, "typtoout: Cache lookup of type %d failed", type);
return (InvalidOid);
}
/* ----------------
* printtup
* printtup
* ----------------
*/
void
printtup(HeapTuple tuple, TupleDesc typeinfo)
{
int i, j, k;
char *outputstr, *attr;
bool isnull;
Oid typoutput;
/* ----------------
* tell the frontend to expect new tuple data
* ----------------
*/
pq_putnchar("D", 1);
/* ----------------
* send a bitmap of which attributes are null
* ----------------
*/
j = 0;
k = 1 << 7;
for (i = 0; i < tuple->t_natts; ) {
i++; /* heap_getattr is a macro, so no increment */
attr = heap_getattr(tuple, InvalidBuffer, i, typeinfo, &isnull);
if (!isnull)
j |= k;
k >>= 1;
if (!(i & 7)) {
pq_putint(j, 1);
j = 0;
k = 1 << 7;
int i,
j,
k;
char *outputstr,
*attr;
bool isnull;
Oid typoutput;
/* ----------------
* tell the frontend to expect new tuple data
* ----------------
*/
pq_putnchar("D", 1);
/* ----------------
* send a bitmap of which attributes are null
* ----------------
*/
j = 0;
k = 1 << 7;
for (i = 0; i < tuple->t_natts;)
{
i++; /* heap_getattr is a macro, so no
* increment */
attr = heap_getattr(tuple, InvalidBuffer, i, typeinfo, &isnull);
if (!isnull)
j |= k;
k >>= 1;
if (!(i & 7))
{
pq_putint(j, 1);
j = 0;
k = 1 << 7;
}
}
}
if (i & 7)
pq_putint(j, 1);
/* ----------------
* send the attributes of this tuple
* ----------------
*/
for (i = 0; i < tuple->t_natts; ++i) {
attr = heap_getattr(tuple, InvalidBuffer, i+1, typeinfo, &isnull);
typoutput = typtoout((Oid) typeinfo->attrs[i]->atttypid);
if (!isnull && OidIsValid(typoutput)) {
outputstr = fmgr(typoutput, attr,
gettypelem(typeinfo->attrs[i]->atttypid));
pq_putint(strlen(outputstr)+4, 4);
pq_putnchar(outputstr, strlen(outputstr));
pfree(outputstr);
if (i & 7)
pq_putint(j, 1);
/* ----------------
* send the attributes of this tuple
* ----------------
*/
for (i = 0; i < tuple->t_natts; ++i)
{
attr = heap_getattr(tuple, InvalidBuffer, i + 1, typeinfo, &isnull);
typoutput = typtoout((Oid) typeinfo->attrs[i]->atttypid);
if (!isnull && OidIsValid(typoutput))
{
outputstr = fmgr(typoutput, attr,
gettypelem(typeinfo->attrs[i]->atttypid));
pq_putint(strlen(outputstr) + 4, 4);
pq_putnchar(outputstr, strlen(outputstr));
pfree(outputstr);
}
}
}
}
/* ----------------
* printatt
* printatt
* ----------------
*/
static void
printatt(unsigned attributeId,
AttributeTupleForm attributeP,
char *value)
AttributeTupleForm attributeP,
char *value)
{
printf("\t%2d: %s%s%s%s\t(typeid = %u, len = %d, byval = %c)\n",
attributeId,
attributeP->attname.data,
value != NULL ? " = \"" : "",
value != NULL ? value : "",
value != NULL ? "\"" : "",
(unsigned int) (attributeP->atttypid),
attributeP->attlen,
attributeP->attbyval ? 't' : 'f');
printf("\t%2d: %s%s%s%s\t(typeid = %u, len = %d, byval = %c)\n",
attributeId,
attributeP->attname.data,
value != NULL ? " = \"" : "",
value != NULL ? value : "",
value != NULL ? "\"" : "",
(unsigned int) (attributeP->atttypid),
attributeP->attlen,
attributeP->attbyval ? 't' : 'f');
}
/* ----------------
* showatts
* showatts
* ----------------
*/
void
showatts(char *name, TupleDesc tupleDesc)
{
int i;
int natts = tupleDesc->natts;
AttributeTupleForm *attinfo = tupleDesc->attrs;
int i;
int natts = tupleDesc->natts;
AttributeTupleForm *attinfo = tupleDesc->attrs;
puts(name);
for (i = 0; i < natts; ++i)
printatt((unsigned) i+1, attinfo[i], (char *) NULL);
printf("\t----\n");
puts(name);
for (i = 0; i < natts; ++i)
printatt((unsigned) i + 1, attinfo[i], (char *) NULL);
printf("\t----\n");
}
/* ----------------
* debugtup
* debugtup
* ----------------
*/
void
debugtup(HeapTuple tuple, TupleDesc typeinfo)
{
register int i;
char *attr, *value;
bool isnull;
Oid typoutput;
for (i = 0; i < tuple->t_natts; ++i) {
attr = heap_getattr(tuple, InvalidBuffer, i+1, typeinfo, &isnull);
typoutput = typtoout((Oid) typeinfo->attrs[i]->atttypid);
if (!isnull && OidIsValid(typoutput)) {
value = fmgr(typoutput, attr,
gettypelem(typeinfo->attrs[i]->atttypid));
printatt((unsigned) i+1, typeinfo->attrs[i], value);
pfree(value);
register int i;
char *attr,
*value;
bool isnull;
Oid typoutput;
for (i = 0; i < tuple->t_natts; ++i)
{
attr = heap_getattr(tuple, InvalidBuffer, i + 1, typeinfo, &isnull);
typoutput = typtoout((Oid) typeinfo->attrs[i]->atttypid);
if (!isnull && OidIsValid(typoutput))
{
value = fmgr(typoutput, attr,
gettypelem(typeinfo->attrs[i]->atttypid));
printatt((unsigned) i + 1, typeinfo->attrs[i], value);
pfree(value);
}
}
}
printf("\t----\n");
printf("\t----\n");
}
/* ----------------
* printtup_internal
* Protocol expects either T, D, C, E, or N.
* We use a different data prefix, e.g. 'B' instead of 'D' to
* indicate a tuple in internal (binary) form.
* printtup_internal
* Protocol expects either T, D, C, E, or N.
* We use a different data prefix, e.g. 'B' instead of 'D' to
* 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
printtup_internal(HeapTuple tuple, TupleDesc typeinfo)
{
int i, j, k;
char *attr;
bool isnull;
/* ----------------
* tell the frontend to expect new tuple data
* ----------------
*/
pq_putnchar("B", 1);
/* ----------------
* send a bitmap of which attributes are null
* ----------------
*/
j = 0;
k = 1 << 7;
for (i = 0; i < tuple->t_natts; ) {
i++; /* heap_getattr is a macro, so no increment */
attr = heap_getattr(tuple, InvalidBuffer, i, typeinfo, &isnull);
if (!isnull)
j |= k;
k >>= 1;
if (!(i & 7)) {
pq_putint(j, 1);
j = 0;
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
int i,
j,
k;
char *attr;
bool isnull;
/* ----------------
* tell the frontend to expect new tuple data
* ----------------
*/
pq_putnchar("B", 1);
/* ----------------
* send a bitmap of which attributes are null
* ----------------
*/
j = 0;
k = 1 << 7;
for (i = 0; i < tuple->t_natts;)
{
i++; /* heap_getattr is a macro, so no
* increment */
attr = heap_getattr(tuple, InvalidBuffer, i, typeinfo, &isnull);
if (!isnull)
j |= k;
k >>= 1;
if (!(i & 7))
{
char *d = VARDATA(attr);
fprintf(stderr, "length %d data %x%x%x%x\n",
len, *d, *(d+1), *(d+2), *(d+3));
pq_putint(j, 1);
j = 0;
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);
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 direction and key code
* scan direction and key code
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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 --
* True iff the scan key entry is legal.
* True iff the scan key entry is legal.
*/
#define ScanKeyEntryIsLegal(entry) \
((bool) (AssertMacro(PointerIsValid(entry)) && \
AttributeNumberIsValid(entry->sk_attno)))
((bool) (AssertMacro(PointerIsValid(entry)) && \
AttributeNumberIsValid(entry->sk_attno)))
/*
* ScanKeyEntrySetIllegal --
* Marks a scan key entry as illegal.
* Marks a scan key entry as illegal.
*/
void
ScanKeyEntrySetIllegal(ScanKey entry)
{
Assert(PointerIsValid(entry));
entry->sk_flags = 0; /* just in case... */
entry->sk_attno = InvalidAttrNumber;
entry->sk_procedure = 0; /* should be InvalidRegProcedure */
Assert(PointerIsValid(entry));
entry->sk_flags = 0; /* just in case... */
entry->sk_attno = InvalidAttrNumber;
entry->sk_procedure = 0; /* should be InvalidRegProcedure */
}
/*
* ScanKeyEntryInitialize --
* Initializes an scan key entry.
* Initializes an scan key entry.
*
* Note:
* Assumes the scan key entry is valid.
* Assumes the intialized scan key entry will be legal.
* Assumes the scan key entry is valid.
* Assumes the intialized scan key entry will be legal.
*/
void
ScanKeyEntryInitialize(ScanKey entry,
bits16 flags,
AttrNumber attributeNumber,
RegProcedure procedure,
Datum argument)
bits16 flags,
AttrNumber attributeNumber,
RegProcedure procedure,
Datum argument)
{
Assert(PointerIsValid(entry));
entry->sk_flags = flags;
entry->sk_attno = attributeNumber;
entry->sk_procedure = procedure;
entry->sk_argument = argument;
fmgr_info(procedure, &entry->sk_func, &entry->sk_nargs);
Assert(ScanKeyEntryIsLegal(entry));
Assert(PointerIsValid(entry));
entry->sk_flags = flags;
entry->sk_attno = attributeNumber;
entry->sk_procedure = procedure;
entry->sk_argument = argument;
fmgr_info(procedure, &entry->sk_func, &entry->sk_nargs);
Assert(ScanKeyEntryIsLegal(entry));
}

View File

@ -1,17 +1,17 @@
/*-------------------------------------------------------------------------
*
* tupdesc.c--
* POSTGRES tuple descriptor support code
* POSTGRES tuple descriptor support code
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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
* some of the executor utility code such as "ExecTypeFromTL" should be
* moved here.
* some of the executor utility code such as "ExecTypeFromTL" should be
* moved here.
*
*-------------------------------------------------------------------------
*/
@ -28,518 +28,534 @@
#include <utils/syscache.h>
#ifndef HAVE_MEMMOVE
# include <regex/utils.h>
#include <regex/utils.h>
#else
# include <string.h>
#include <string.h>
#endif
/* ----------------------------------------------------------------
* CreateTemplateTupleDesc
* CreateTemplateTupleDesc
*
* This function allocates and zeros a tuple descriptor structure.
* This function allocates and zeros a tuple descriptor structure.
* ----------------------------------------------------------------
*/
TupleDesc
CreateTemplateTupleDesc(int natts)
{
uint32 size;
TupleDesc desc;
/* ----------------
* sanity checks
* ----------------
*/
AssertArg(natts >= 1);
/* ----------------
* allocate enough memory for the tuple descriptor and
* zero it as TupleDescInitEntry assumes that the descriptor
* is filled with NULL pointers.
* ----------------
*/
size = natts * sizeof (AttributeTupleForm);
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
desc->attrs = (AttributeTupleForm*) palloc(size);
desc->constr = NULL;
memset(desc->attrs, 0, size);
uint32 size;
TupleDesc desc;
desc->natts = natts;
/* ----------------
* sanity checks
* ----------------
*/
AssertArg(natts >= 1);
return (desc);
/* ----------------
* allocate enough memory for the tuple descriptor and
* zero it as TupleDescInitEntry assumes that the descriptor
* is filled with NULL pointers.
* ----------------
*/
size = natts * sizeof(AttributeTupleForm);
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
desc->attrs = (AttributeTupleForm *) palloc(size);
desc->constr = NULL;
memset(desc->attrs, 0, size);
desc->natts = natts;
return (desc);
}
/* ----------------------------------------------------------------
* CreateTupleDesc
* CreateTupleDesc
*
* This function allocates a new TupleDesc from AttributeTupleForm array
* This function allocates a new TupleDesc from AttributeTupleForm array
* ----------------------------------------------------------------
*/
TupleDesc
CreateTupleDesc(int natts, AttributeTupleForm* attrs)
CreateTupleDesc(int natts, AttributeTupleForm * attrs)
{
TupleDesc desc;
/* ----------------
* sanity checks
* ----------------
*/
AssertArg(natts >= 1);
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
desc->attrs = attrs;
desc->natts = natts;
desc->constr = NULL;
TupleDesc desc;
return (desc);
/* ----------------
* sanity checks
* ----------------
*/
AssertArg(natts >= 1);
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
desc->attrs = attrs;
desc->natts = natts;
desc->constr = NULL;
return (desc);
}
/* ----------------------------------------------------------------
* CreateTupleDescCopy
* CreateTupleDescCopy
*
* This function creates a new TupleDesc by copying from an existing
* TupleDesc
*
* !!! Constraints are not copied !!!
* This function creates a new TupleDesc by copying from an existing
* TupleDesc
*
* !!! Constraints are not copied !!!
* ----------------------------------------------------------------
*/
TupleDesc
CreateTupleDescCopy(TupleDesc tupdesc)
{
TupleDesc desc;
int i, size;
TupleDesc desc;
int i,
size;
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
desc->natts = tupdesc->natts;
size = desc->natts * sizeof (AttributeTupleForm);
desc->attrs = (AttributeTupleForm*) palloc(size);
for (i=0;i<desc->natts;i++) {
desc->attrs[i] =
(AttributeTupleForm)palloc(ATTRIBUTE_TUPLE_SIZE);
memmove(desc->attrs[i],
tupdesc->attrs[i],
ATTRIBUTE_TUPLE_SIZE);
desc->attrs[i]->attnotnull = false;
desc->attrs[i]->atthasdef = false;
}
desc->constr = NULL;
return desc;
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
desc->natts = tupdesc->natts;
size = desc->natts * sizeof(AttributeTupleForm);
desc->attrs = (AttributeTupleForm *) palloc(size);
for (i = 0; i < desc->natts; i++)
{
desc->attrs[i] =
(AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE);
memmove(desc->attrs[i],
tupdesc->attrs[i],
ATTRIBUTE_TUPLE_SIZE);
desc->attrs[i]->attnotnull = false;
desc->attrs[i]->atthasdef = false;
}
desc->constr = NULL;
return desc;
}
/* ----------------------------------------------------------------
* CreateTupleDescCopyConstr
* CreateTupleDescCopyConstr
*
* This function creates a new TupleDesc by copying from an existing
* TupleDesc (with Constraints)
*
* This function creates a new TupleDesc by copying from an existing
* TupleDesc (with Constraints)
*
* ----------------------------------------------------------------
*/
TupleDesc
CreateTupleDescCopyConstr(TupleDesc tupdesc)
{
TupleDesc desc;
TupleConstr *constr = tupdesc->constr;
int i, size;
TupleDesc desc;
TupleConstr *constr = tupdesc->constr;
int i,
size;
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
desc->natts = tupdesc->natts;
size = desc->natts * sizeof (AttributeTupleForm);
desc->attrs = (AttributeTupleForm*) palloc(size);
for (i=0;i<desc->natts;i++) {
desc->attrs[i] =
(AttributeTupleForm)palloc(ATTRIBUTE_TUPLE_SIZE);
memmove(desc->attrs[i],
tupdesc->attrs[i],
ATTRIBUTE_TUPLE_SIZE);
}
if (constr)
{
TupleConstr *cpy = (TupleConstr *) palloc(sizeof(TupleConstr));
cpy->has_not_null = constr->has_not_null;
if ( ( cpy->num_defval = constr->num_defval ) > 0 )
{
cpy->defval = (AttrDefault *) palloc (cpy->num_defval * sizeof (AttrDefault));
memcpy (cpy->defval, constr->defval, cpy->num_defval * sizeof (AttrDefault));
for (i = cpy->num_defval - 1; i >= 0; i--)
{
if ( constr->defval[i].adbin )
cpy->defval[i].adbin = pstrdup (constr->defval[i].adbin);
if ( constr->defval[i].adsrc )
cpy->defval[i].adsrc = pstrdup (constr->defval[i].adsrc);
}
}
if ( ( cpy->num_check = constr->num_check ) > 0 )
{
cpy->check = (ConstrCheck *) palloc (cpy->num_check * sizeof (ConstrCheck));
memcpy (cpy->check, constr->check, cpy->num_check * sizeof (ConstrCheck));
for (i = cpy->num_check - 1; i >= 0; i--)
{
if ( constr->check[i].ccname )
cpy->check[i].ccname = pstrdup (constr->check[i].ccname);
if ( constr->check[i].ccbin )
cpy->check[i].ccbin = pstrdup (constr->check[i].ccbin);
if ( constr->check[i].ccsrc )
cpy->check[i].ccsrc = pstrdup (constr->check[i].ccsrc);
}
}
desc->constr = cpy;
}
else
desc->constr = NULL;
return desc;
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
desc->natts = tupdesc->natts;
size = desc->natts * sizeof(AttributeTupleForm);
desc->attrs = (AttributeTupleForm *) palloc(size);
for (i = 0; i < desc->natts; i++)
{
desc->attrs[i] =
(AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE);
memmove(desc->attrs[i],
tupdesc->attrs[i],
ATTRIBUTE_TUPLE_SIZE);
}
if (constr)
{
TupleConstr *cpy = (TupleConstr *) palloc(sizeof(TupleConstr));
cpy->has_not_null = constr->has_not_null;
if ((cpy->num_defval = constr->num_defval) > 0)
{
cpy->defval = (AttrDefault *) palloc(cpy->num_defval * sizeof(AttrDefault));
memcpy(cpy->defval, constr->defval, cpy->num_defval * sizeof(AttrDefault));
for (i = cpy->num_defval - 1; i >= 0; i--)
{
if (constr->defval[i].adbin)
cpy->defval[i].adbin = pstrdup(constr->defval[i].adbin);
if (constr->defval[i].adsrc)
cpy->defval[i].adsrc = pstrdup(constr->defval[i].adsrc);
}
}
if ((cpy->num_check = constr->num_check) > 0)
{
cpy->check = (ConstrCheck *) palloc(cpy->num_check * sizeof(ConstrCheck));
memcpy(cpy->check, constr->check, cpy->num_check * sizeof(ConstrCheck));
for (i = cpy->num_check - 1; i >= 0; i--)
{
if (constr->check[i].ccname)
cpy->check[i].ccname = pstrdup(constr->check[i].ccname);
if (constr->check[i].ccbin)
cpy->check[i].ccbin = pstrdup(constr->check[i].ccbin);
if (constr->check[i].ccsrc)
cpy->check[i].ccsrc = pstrdup(constr->check[i].ccsrc);
}
}
desc->constr = cpy;
}
else
desc->constr = NULL;
return desc;
}
void
FreeTupleDesc (TupleDesc tupdesc)
FreeTupleDesc(TupleDesc tupdesc)
{
int i;
for (i = 0; i < tupdesc->natts; i++)
pfree (tupdesc->attrs[i]);
pfree (tupdesc->attrs);
if ( tupdesc->constr )
{
if ( tupdesc->constr->num_defval > 0 )
{
AttrDefault *attrdef = tupdesc->constr->defval;
for (i = tupdesc->constr->num_defval - 1; i >= 0; i--)
{
if ( attrdef[i].adbin )
pfree (attrdef[i].adbin);
if ( attrdef[i].adsrc )
pfree (attrdef[i].adsrc);
}
pfree (attrdef);
}
if ( tupdesc->constr->num_check > 0 )
{
ConstrCheck *check = tupdesc->constr->check;
for (i = tupdesc->constr->num_check - 1; i >= 0; i--)
{
if ( check[i].ccname )
pfree (check[i].ccname);
if ( check[i].ccbin )
pfree (check[i].ccbin);
if ( check[i].ccsrc )
pfree (check[i].ccsrc);
}
pfree (check);
}
pfree (tupdesc->constr);
}
pfree (tupdesc);
int i;
for (i = 0; i < tupdesc->natts; i++)
pfree(tupdesc->attrs[i]);
pfree(tupdesc->attrs);
if (tupdesc->constr)
{
if (tupdesc->constr->num_defval > 0)
{
AttrDefault *attrdef = tupdesc->constr->defval;
for (i = tupdesc->constr->num_defval - 1; i >= 0; i--)
{
if (attrdef[i].adbin)
pfree(attrdef[i].adbin);
if (attrdef[i].adsrc)
pfree(attrdef[i].adsrc);
}
pfree(attrdef);
}
if (tupdesc->constr->num_check > 0)
{
ConstrCheck *check = tupdesc->constr->check;
for (i = tupdesc->constr->num_check - 1; i >= 0; i--)
{
if (check[i].ccname)
pfree(check[i].ccname);
if (check[i].ccbin)
pfree(check[i].ccbin);
if (check[i].ccsrc)
pfree(check[i].ccsrc);
}
pfree(check);
}
pfree(tupdesc->constr);
}
pfree(tupdesc);
}
/* ----------------------------------------------------------------
* TupleDescInitEntry
* TupleDescInitEntry
*
* This function initializes a single attribute structure in
* a preallocated tuple descriptor.
* This function initializes a single attribute structure in
* a preallocated tuple descriptor.
* ----------------------------------------------------------------
*/
bool
TupleDescInitEntry(TupleDesc desc,
AttrNumber attributeNumber,
char *attributeName,
char *typeName,
int attdim,
bool attisset)
AttrNumber attributeNumber,
char *attributeName,
char *typeName,
int attdim,
bool attisset)
{
HeapTuple tuple;
TypeTupleForm typeForm;
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]));
HeapTuple tuple;
TypeTupleForm typeForm;
AttributeTupleForm att;
/* ----------------
* 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.
* sanity checks
* ----------------
*/
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;
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
* 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
* manager in "create EMP (name=text, manager = EMP)".
* It calls TypeShellMake() which inserts a "shell" type
* 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
* above in TupleDescInitEntry.
* This function initializes a "self-referential" attribute like
* manager in "create EMP (name=text, manager = EMP)".
* It calls TypeShellMake() which inserts a "shell" type
* 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
* above in TupleDescInitEntry.
* ----------------------------------------------------------------
*/
static void
TupleDescMakeSelfReference(TupleDesc desc,
AttrNumber attnum,
char *relname)
AttrNumber attnum,
char *relname)
{
AttributeTupleForm att;
Type t = type("oid");
att = desc->attrs[attnum-1];
att->atttypid = TypeShellMake(relname);
att->attlen = tlen(t);
att->attbyval = tbyval(t);
att->attnelems = 0;
AttributeTupleForm att;
Type t = type("oid");
att = desc->attrs[attnum - 1];
att->atttypid = TypeShellMake(relname);
att->attlen = tlen(t);
att->attbyval = tbyval(t);
att->attnelems = 0;
}
/* ----------------------------------------------------------------
* BuildDescForRelation
* BuildDescForRelation
*
* This is a general purpose function identical to BuildDesc
* but is used by the DefineRelation() code to catch the
* special case where you
* This is a general purpose function identical to BuildDesc
* but is used by the DefineRelation() code to catch the
* special case where you
*
* create FOO ( ..., x = FOO )
* create FOO ( ..., x = FOO )
*
* here, the initial type lookup for "x = FOO" will fail
* because FOO isn't in the catalogs yet. But since we
* are creating FOO, instead of doing an elog() we add
* a shell type tuple to pg_type and fix things later
* in amcreate().
* here, the initial type lookup for "x = FOO" will fail
* because FOO isn't in the catalogs yet. But since we
* are creating FOO, instead of doing an elog() we add
* a shell type tuple to pg_type and fix things later
* in amcreate().
* ----------------------------------------------------------------
*/
TupleDesc
BuildDescForRelation(List *schema, char *relname)
BuildDescForRelation(List * schema, char *relname)
{
int natts;
AttrNumber attnum;
List *p;
TupleDesc desc;
AttrDefault *attrdef = NULL;
TupleConstr *constr = (TupleConstr *) palloc(sizeof(TupleConstr));
char *attname;
char *typename;
int attdim;
int ndef = 0;
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;
int natts;
AttrNumber attnum;
List *p;
TupleDesc desc;
AttrDefault *attrdef = NULL;
TupleConstr *constr = (TupleConstr *) palloc(sizeof(TupleConstr));
char *attname;
char *typename;
int attdim;
int ndef = 0;
bool attisset;
/* ----------------
* for each entry in the list, get the name and type
* information from the list and have TupleDescInitEntry
* fill in the attribute information we need.
* allocate a new tuple descriptor
* ----------------
*/
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;
}
natts = length(schema);
desc = CreateTemplateTupleDesc(natts);
constr->has_not_null = false;
/* 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 )
attnum = 0;
typename = palloc(NAMEDATALEN);
foreach(p, schema)
{
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;
ColumnDef *entry;
List *arry;
/* ----------------
* for each entry in the list, get the name and type
* information from the list and have TupleDescInitEntry
* 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 ( constr->has_not_null || ndef > 0 )
{
desc->constr = constr;
if ( ndef > 0 ) /* DEFAULTs */
{
if ( ndef < natts )
constr->defval = (AttrDefault*)
repalloc (attrdef, ndef * sizeof (AttrDefault));
else
constr->defval = attrdef;
constr->num_defval = ndef;
}
else
constr->num_defval = 0;
constr->num_check = 0;
}
else
{
pfree (constr);
desc->constr = NULL;
}
return desc;
if (ndef > 0) /* DEFAULTs */
{
if (ndef < natts)
constr->defval = (AttrDefault *)
repalloc(attrdef, ndef * sizeof(AttrDefault));
else
constr->defval = attrdef;
constr->num_defval = ndef;
}
else
constr->num_defval = 0;
constr->num_check = 0;
}
else
{
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--
* fetch tuples from a GiST scan.
* fetch tuples from a GiST scan.
*
*
*
* 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>
#ifndef HAVE_MEMMOVE
# include <regex/utils.h>
#include <regex/utils.h>
#else
# include <string.h>
#include <string.h>
#endif
static OffsetNumber gistfindnext(IndexScanDesc s, Page p, OffsetNumber n,
ScanDirection dir);
static OffsetNumber
gistfindnext(IndexScanDesc s, Page p, OffsetNumber n,
ScanDirection dir);
static RetrieveIndexResult gistscancache(IndexScanDesc s, ScanDirection dir);
static RetrieveIndexResult gistfirst(IndexScanDesc s, ScanDirection dir);
static RetrieveIndexResult gistnext(IndexScanDesc s, ScanDirection dir);
static ItemPointer gistheapptr(Relation r, ItemPointer itemp);
static bool gistindex_keytest(IndexTuple tuple, TupleDesc tupdesc,
int scanKeySize, ScanKey key, GISTSTATE *giststate,
Relation r, Page p, OffsetNumber offset);
static bool
gistindex_keytest(IndexTuple tuple, TupleDesc tupdesc,
int scanKeySize, ScanKey key, GISTSTATE * giststate,
Relation r, Page p, OffsetNumber offset);
RetrieveIndexResult
gistgettuple(IndexScanDesc s, ScanDirection dir)
{
RetrieveIndexResult res;
/* if we have it cached in the scan desc, just return the value */
if ((res = gistscancache(s, dir)) != (RetrieveIndexResult) NULL)
RetrieveIndexResult res;
/* if we have it cached in the scan desc, just return the value */
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);
/* 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)
{
Buffer b;
Page p;
OffsetNumber n;
OffsetNumber maxoff;
RetrieveIndexResult res;
GISTPageOpaque po;
GISTScanOpaque so;
GISTSTACK *stk;
BlockNumber blk;
IndexTuple it;
Buffer b;
Page p;
OffsetNumber n;
OffsetNumber maxoff;
RetrieveIndexResult res;
GISTPageOpaque po;
GISTScanOpaque so;
GISTSTACK *stk;
BlockNumber blk;
IndexTuple it;
b = ReadBuffer(s->relation, GISTP_ROOT);
p = BufferGetPage(b);
po = (GISTPageOpaque) PageGetSpecialPointer(p);
so = (GISTScanOpaque) s->opaque;
b = ReadBuffer(s->relation, GISTP_ROOT);
p = BufferGetPage(b);
po = (GISTPageOpaque) PageGetSpecialPointer(p);
so = (GISTScanOpaque) s->opaque;
for (;;) {
maxoff = PageGetMaxOffsetNumber(p);
if (ScanDirectionIsBackward(dir))
n = gistfindnext(s, p, maxoff, dir);
else
n = gistfindnext(s, p, FirstOffsetNumber, 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);
po = (GISTPageOpaque) PageGetSpecialPointer(p);
maxoff = PageGetMaxOffsetNumber(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);
for (;;)
{
maxoff = PageGetMaxOffsetNumber(p);
if (ScanDirectionIsBackward(dir))
n = gistfindnext(s, p, maxoff, dir);
else
n = gistfindnext(s, p, FirstOffsetNumber, 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);
po = (GISTPageOpaque) PageGetSpecialPointer(p);
maxoff = PageGetMaxOffsetNumber(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)
{
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)
{
Buffer b;
Page p;
OffsetNumber n;
OffsetNumber maxoff;
RetrieveIndexResult res;
GISTPageOpaque po;
GISTScanOpaque so;
GISTSTACK *stk;
BlockNumber blk;
IndexTuple it;
blk = ItemPointerGetBlockNumber(&(s->currentItemData));
n = ItemPointerGetOffsetNumber(&(s->currentItemData));
if (ScanDirectionIsForward(dir)) {
n = OffsetNumberNext(n);
} else {
n = OffsetNumberPrev(n);
}
Buffer b;
Page p;
OffsetNumber n;
OffsetNumber maxoff;
RetrieveIndexResult res;
GISTPageOpaque po;
GISTScanOpaque so;
GISTSTACK *stk;
BlockNumber blk;
IndexTuple it;
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);
blk = ItemPointerGetBlockNumber(&(s->currentItemData));
n = ItemPointerGetOffsetNumber(&(s->currentItemData));
if (ScanDirectionIsForward(dir))
{
n = OffsetNumberNext(n);
}
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;
}
else
{
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)
{
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 */
static bool
static bool
gistindex_keytest(IndexTuple tuple,
TupleDesc tupdesc,
int scanKeySize,
ScanKey key,
GISTSTATE *giststate,
Relation r,
Page p,
OffsetNumber offset)
TupleDesc tupdesc,
int scanKeySize,
ScanKey key,
GISTSTATE * giststate,
Relation r,
Page p,
OffsetNumber offset)
{
bool isNull;
Datum datum;
int test;
GISTENTRY de;
bool isNull;
Datum datum;
int test;
GISTENTRY de;
IncrIndexProcessed();
IncrIndexProcessed();
while (scanKeySize > 0) {
datum = index_getattr(tuple,
1,
tupdesc,
&isNull);
gistdentryinit(giststate, &de, (char *)datum, r, p, offset,
IndexTupleSize(tuple) - sizeof(IndexTupleData),
FALSE);
if (isNull) {
/* XXX eventually should check if SK_ISNULL */
return (false);
while (scanKeySize > 0)
{
datum = index_getattr(tuple,
1,
tupdesc,
&isNull);
gistdentryinit(giststate, &de, (char *) datum, r, p, offset,
IndexTupleSize(tuple) - sizeof(IndexTupleData),
FALSE);
if (isNull)
{
/* 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) {
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);
return (true);
}
static OffsetNumber
static OffsetNumber
gistfindnext(IndexScanDesc s, Page p, OffsetNumber n, ScanDirection dir)
{
OffsetNumber maxoff;
char *it;
GISTPageOpaque po;
GISTScanOpaque so;
GISTSTATE *giststate;
OffsetNumber maxoff;
char *it;
GISTPageOpaque po;
GISTScanOpaque so;
GISTSTATE *giststate;
maxoff = PageGetMaxOffsetNumber(p);
po = (GISTPageOpaque) PageGetSpecialPointer(p);
so = (GISTScanOpaque) s->opaque;
giststate = so->giststate;
maxoff = PageGetMaxOffsetNumber(p);
po = (GISTPageOpaque) PageGetSpecialPointer(p);
so = (GISTScanOpaque) s->opaque;
giststate = so->giststate;
/*
* 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.
*/
if (so->s_flags & GS_CURBEFORE) {
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);
/*
* 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.
*/
if (so->s_flags & GS_CURBEFORE)
{
so->s_flags &= ~GS_CURBEFORE;
n = OffsetNumberPrev(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)
{
RetrieveIndexResult res;
ItemPointer ip;
if (!(ScanDirectionIsNoMovement(dir)
&& ItemPointerIsValid(&(s->currentItemData)))) {
return ((RetrieveIndexResult) NULL);
}
ip = gistheapptr(s->relation, &(s->currentItemData));
if (ItemPointerIsValid(ip))
res = FormRetrieveIndexResult(&(s->currentItemData), ip);
else
res = (RetrieveIndexResult) NULL;
RetrieveIndexResult res;
ItemPointer ip;
pfree (ip);
return (res);
if (!(ScanDirectionIsNoMovement(dir)
&& ItemPointerIsValid(&(s->currentItemData))))
{
return ((RetrieveIndexResult) NULL);
}
ip = gistheapptr(s->relation, &(s->currentItemData));
if (ItemPointerIsValid(ip))
res = FormRetrieveIndexResult(&(s->currentItemData), ip);
else
res = (RetrieveIndexResult) NULL;
pfree(ip);
return (res);
}
/*
* gistheapptr returns the item pointer to the tuple in the heap relation
* for which itemp is the index relation item pointer.
* gistheapptr returns the item pointer to the tuple in the heap relation
* for which itemp is the index relation item pointer.
*/
static ItemPointer
static ItemPointer
gistheapptr(Relation r, ItemPointer itemp)
{
Buffer b;
Page p;
IndexTuple it;
ItemPointer ip;
OffsetNumber n;
ip = (ItemPointer) palloc(sizeof(ItemPointerData));
if (ItemPointerIsValid(itemp)) {
b = ReadBuffer(r, ItemPointerGetBlockNumber(itemp));
p = BufferGetPage(b);
n = ItemPointerGetOffsetNumber(itemp);
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
memmove((char *) ip, (char *) &(it->t_tid),
sizeof(ItemPointerData));
ReleaseBuffer(b);
} else {
ItemPointerSetInvalid(ip);
}
return (ip);
Buffer b;
Page p;
IndexTuple it;
ItemPointer ip;
OffsetNumber n;
ip = (ItemPointer) palloc(sizeof(ItemPointerData));
if (ItemPointerIsValid(itemp))
{
b = ReadBuffer(r, ItemPointerGetBlockNumber(itemp));
p = BufferGetPage(b);
n = ItemPointerGetOffsetNumber(itemp);
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
memmove((char *) ip, (char *) &(it->t_tid),
sizeof(ItemPointerData));
ReleaseBuffer(b);
}
else
{
ItemPointerSetInvalid(ip);
}
return (ip);
}

View File

@ -1,11 +1,11 @@
/*-------------------------------------------------------------------------
*
* gistscan.c--
* routines to manage scans on index relations
* routines to manage scans on index relations
*
*
* 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
*
*-------------------------------------------------------------------------
*/
@ -18,375 +18,411 @@
#include <access/rtree.h>
#include <storage/bufmgr.h>
#include <access/giststrat.h>
#include <storage/lmgr.h>
#include <storage/lmgr.h>
#ifndef HAVE_MEMMOVE
# include <regex/utils.h>
#include <regex/utils.h>
#else
# include <string.h>
#include <string.h>
#endif
/* routines defined and used here */
static void gistregscan(IndexScanDesc s);
static void gistdropscan(IndexScanDesc s);
static void gistadjone(IndexScanDesc s, int op, BlockNumber blkno,
OffsetNumber offnum);
static void adjuststack(GISTSTACK *stk, BlockNumber blkno,
static void gistregscan(IndexScanDesc s);
static void gistdropscan(IndexScanDesc s);
static void
gistadjone(IndexScanDesc s, int op, BlockNumber blkno,
OffsetNumber offnum);
static void
adjuststack(GISTSTACK * stk, BlockNumber blkno,
OffsetNumber offnum);
static void adjustiptr(IndexScanDesc s, ItemPointer iptr,
int op, BlockNumber blkno, OffsetNumber offnum);
static void
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
* 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
* 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
* 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.
* 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
* 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
* 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
* locks on the same object, so that's why we need to handle this case.
*/
typedef struct GISTScanListData {
IndexScanDesc gsl_scan;
struct GISTScanListData *gsl_next;
} GISTScanListData;
typedef struct GISTScanListData
{
IndexScanDesc gsl_scan;
struct GISTScanListData *gsl_next;
} GISTScanListData;
typedef GISTScanListData *GISTScanList;
typedef GISTScanListData *GISTScanList;
/* pointer to list of local scans on GiSTs */
static GISTScanList GISTScans = (GISTScanList) NULL;
IndexScanDesc
gistbeginscan(Relation r,
bool fromEnd,
uint16 nkeys,
ScanKey key)
bool fromEnd,
uint16 nkeys,
ScanKey key)
{
IndexScanDesc s;
RelationSetLockForRead(r);
s = RelationGetIndexScan(r, fromEnd, nkeys, key);
gistregscan(s);
return (s);
IndexScanDesc s;
RelationSetLockForRead(r);
s = RelationGetIndexScan(r, fromEnd, nkeys, key);
gistregscan(s);
return (s);
}
void
gistrescan(IndexScanDesc s, bool fromEnd, ScanKey key)
{
GISTScanOpaque p;
int i;
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++)
GISTScanOpaque p;
int i;
if (!IndexScanIsValid(s))
{
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;
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
= 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;
}
}
} 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
gistmarkpos(IndexScanDesc s)
{
GISTScanOpaque p;
GISTSTACK *o, *n, *tmp;
s->currentMarkData = s->currentItemData;
p = (GISTScanOpaque) s->opaque;
if (p->s_flags & GS_CURBEFORE)
p->s_flags |= GS_MRKBEFORE;
else
p->s_flags &= ~GS_MRKBEFORE;
o = (GISTSTACK *) NULL;
n = p->s_stack;
/* copy the parent stack from the current item data */
while (n != (GISTSTACK *) NULL) {
tmp = (GISTSTACK *) palloc(sizeof(GISTSTACK));
tmp->gs_child = n->gs_child;
tmp->gs_blk = n->gs_blk;
tmp->gs_parent = o;
o = tmp;
n = n->gs_parent;
}
gistfreestack(p->s_markstk);
p->s_markstk = o;
GISTScanOpaque p;
GISTSTACK *o,
*n,
*tmp;
s->currentMarkData = s->currentItemData;
p = (GISTScanOpaque) s->opaque;
if (p->s_flags & GS_CURBEFORE)
p->s_flags |= GS_MRKBEFORE;
else
p->s_flags &= ~GS_MRKBEFORE;
o = (GISTSTACK *) NULL;
n = p->s_stack;
/* copy the parent stack from the current item data */
while (n != (GISTSTACK *) NULL)
{
tmp = (GISTSTACK *) palloc(sizeof(GISTSTACK));
tmp->gs_child = n->gs_child;
tmp->gs_blk = n->gs_blk;
tmp->gs_parent = o;
o = tmp;
n = n->gs_parent;
}
gistfreestack(p->s_markstk);
p->s_markstk = o;
}
void
gistrestrpos(IndexScanDesc s)
{
GISTScanOpaque p;
GISTSTACK *o, *n, *tmp;
s->currentItemData = s->currentMarkData;
p = (GISTScanOpaque) s->opaque;
if (p->s_flags & GS_MRKBEFORE)
p->s_flags |= GS_CURBEFORE;
else
p->s_flags &= ~GS_CURBEFORE;
o = (GISTSTACK *) NULL;
n = p->s_markstk;
/* copy the parent stack from the current item data */
while (n != (GISTSTACK *) NULL) {
tmp = (GISTSTACK *) palloc(sizeof(GISTSTACK));
tmp->gs_child = n->gs_child;
tmp->gs_blk = n->gs_blk;
tmp->gs_parent = o;
o = tmp;
n = n->gs_parent;
}
gistfreestack(p->s_stack);
p->s_stack = o;
GISTScanOpaque p;
GISTSTACK *o,
*n,
*tmp;
s->currentItemData = s->currentMarkData;
p = (GISTScanOpaque) s->opaque;
if (p->s_flags & GS_MRKBEFORE)
p->s_flags |= GS_CURBEFORE;
else
p->s_flags &= ~GS_CURBEFORE;
o = (GISTSTACK *) NULL;
n = p->s_markstk;
/* copy the parent stack from the current item data */
while (n != (GISTSTACK *) NULL)
{
tmp = (GISTSTACK *) palloc(sizeof(GISTSTACK));
tmp->gs_child = n->gs_child;
tmp->gs_blk = n->gs_blk;
tmp->gs_parent = o;
o = tmp;
n = n->gs_parent;
}
gistfreestack(p->s_stack);
p->s_stack = o;
}
void
gistendscan(IndexScanDesc s)
{
GISTScanOpaque p;
p = (GISTScanOpaque) s->opaque;
if (p != (GISTScanOpaque) NULL) {
gistfreestack(p->s_stack);
gistfreestack(p->s_markstk);
pfree (s->opaque);
}
gistdropscan(s);
/* XXX don't unset read lock -- two-phase locking */
GISTScanOpaque p;
p = (GISTScanOpaque) s->opaque;
if (p != (GISTScanOpaque) NULL)
{
gistfreestack(p->s_stack);
gistfreestack(p->s_markstk);
pfree(s->opaque);
}
gistdropscan(s);
/* XXX don't unset read lock -- two-phase locking */
}
static void
gistregscan(IndexScanDesc s)
{
GISTScanList l;
l = (GISTScanList) palloc(sizeof(GISTScanListData));
l->gsl_scan = s;
l->gsl_next = GISTScans;
GISTScans = l;
GISTScanList l;
l = (GISTScanList) palloc(sizeof(GISTScanListData));
l->gsl_scan = s;
l->gsl_next = GISTScans;
GISTScans = l;
}
static void
gistdropscan(IndexScanDesc s)
{
GISTScanList l;
GISTScanList prev;
prev = (GISTScanList) NULL;
for (l = GISTScans;
l != (GISTScanList) NULL && l->gsl_scan != s;
l = l->gsl_next) {
prev = l;
}
if (l == (GISTScanList) NULL)
elog(WARN, "GiST scan list corrupted -- cannot find 0x%lx", s);
if (prev == (GISTScanList) NULL)
GISTScans = l->gsl_next;
else
prev->gsl_next = l->gsl_next;
pfree(l);
GISTScanList l;
GISTScanList prev;
prev = (GISTScanList) NULL;
for (l = GISTScans;
l != (GISTScanList) NULL && l->gsl_scan != s;
l = l->gsl_next)
{
prev = l;
}
if (l == (GISTScanList) NULL)
elog(WARN, "GiST scan list corrupted -- cannot find 0x%lx", s);
if (prev == (GISTScanList) NULL)
GISTScans = l->gsl_next;
else
prev->gsl_next = l->gsl_next;
pfree(l);
}
void
gistadjscans(Relation r, int op, BlockNumber blkno, OffsetNumber offnum)
{
GISTScanList l;
Oid relid;
relid = r->rd_id;
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);
}
GISTScanList l;
Oid relid;
relid = r->rd_id;
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);
}
}
/*
* 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
* us what the modification is, and blkno and offind tell us what
* block and offset index were affected. This routine checks the
* current and marked positions, and the current and marked stacks,
* to see if any stored location needs to be changed because of the
* update. If so, we make the change here.
* 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
* block and offset index were affected. This routine checks the
* current and marked positions, and the current and marked stacks,
* to see if any stored location needs to be changed because of the
* update. If so, we make the change here.
*/
static void
gistadjone(IndexScanDesc s,
int op,
BlockNumber blkno,
OffsetNumber offnum)
int op,
BlockNumber blkno,
OffsetNumber offnum)
{
GISTScanOpaque so;
adjustiptr(s, &(s->currentItemData), op, blkno, offnum);
adjustiptr(s, &(s->currentMarkData), op, blkno, offnum);
so = (GISTScanOpaque) s->opaque;
if (op == GISTOP_SPLIT) {
adjuststack(so->s_stack, blkno, offnum);
adjuststack(so->s_markstk, blkno, offnum);
}
GISTScanOpaque so;
adjustiptr(s, &(s->currentItemData), op, blkno, offnum);
adjustiptr(s, &(s->currentMarkData), op, blkno, offnum);
so = (GISTScanOpaque) s->opaque;
if (op == GISTOP_SPLIT)
{
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
* need to do nothing, to back up one record, or to start over on
* the same page.
* 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
* the same page.
*/
static void
adjustiptr(IndexScanDesc s,
ItemPointer iptr,
int op,
BlockNumber blkno,
OffsetNumber offnum)
ItemPointer iptr,
int op,
BlockNumber blkno,
OffsetNumber offnum)
{
OffsetNumber curoff;
GISTScanOpaque so;
if (ItemPointerIsValid(iptr)) {
if (ItemPointerGetBlockNumber(iptr) == blkno) {
curoff = ItemPointerGetOffsetNumber(iptr);
so = (GISTScanOpaque) s->opaque;
switch (op) {
case GISTOP_DEL:
/* back up one if we need to */
if (curoff >= offnum) {
if (curoff > FirstOffsetNumber) {
/* just adjust the item pointer */
ItemPointerSet(iptr, blkno, OffsetNumberPrev(curoff));
} else {
/* remember that we're before the current tuple */
ItemPointerSet(iptr, blkno, FirstOffsetNumber);
if (iptr == &(s->currentItemData))
so->s_flags |= GS_CURBEFORE;
else
so->s_flags |= GS_MRKBEFORE;
}
OffsetNumber curoff;
GISTScanOpaque so;
if (ItemPointerIsValid(iptr))
{
if (ItemPointerGetBlockNumber(iptr) == blkno)
{
curoff = ItemPointerGetOffsetNumber(iptr);
so = (GISTScanOpaque) s->opaque;
switch (op)
{
case GISTOP_DEL:
/* back up one if we need to */
if (curoff >= offnum)
{
if (curoff > FirstOffsetNumber)
{
/* just adjust the item pointer */
ItemPointerSet(iptr, blkno, OffsetNumberPrev(curoff));
}
else
{
/* remember that we're before the current tuple */
ItemPointerSet(iptr, blkno, FirstOffsetNumber);
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
* the index we're scanning.
* adjuststack() -- adjust the supplied stack for a split on a page in
* the index we're scanning.
*
* 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
* 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
* 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
* are looking at already in this transaction, we ignore the update
* request.
* 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
* 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
* 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
* are looking at already in this transaction, we ignore the update
* request.
*/
/*ARGSUSED*/
static void
adjuststack(GISTSTACK *stk,
BlockNumber blkno,
OffsetNumber offnum)
adjuststack(GISTSTACK * stk,
BlockNumber blkno,
OffsetNumber offnum)
{
while (stk != (GISTSTACK *) NULL) {
if (stk->gs_blk == blkno)
stk->gs_child = FirstOffsetNumber;
stk = stk->gs_parent;
}
while (stk != (GISTSTACK *) NULL)
{
if (stk->gs_blk == blkno)
stk->gs_child = FirstOffsetNumber;
stk = stk->gs_parent;
}
}

View File

@ -1,116 +1,117 @@
/*-------------------------------------------------------------------------
*
* giststrat.c--
* strategy map data for GiSTs.
* strategy map data for GiSTs.
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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
*
*-------------------------------------------------------------------------
*/
#include <postgres.h>
#include <access/gist.h>
#include <access/istrat.h>
/*
* Note: negate, commute, and negatecommute all assume that operators are
* ordered as follows in the strategy map:
* Note: negate, commute, and negatecommute all assume that operators are
* ordered as follows in the strategy map:
*
* contains, contained-by
* contains, contained-by
*
* The negate, commute, and negatecommute arrays are used by the planner
* to plan indexed scans over data that appears in the qualificiation in
* a boolean negation, or whose operands appear in the wrong order. For
* example, if the operator "<%" means "contains", and the user says
* The negate, commute, and negatecommute arrays are used by the planner
* to plan indexed scans over data that appears in the qualificiation in
* a boolean negation, or whose operands appear in the wrong order. For
* 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
* an operator in their operator class for negating <%.
* the planner can plan an index scan by noting that GiST indices have
* 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
* its opclass for commuting <%, and plan the scan using that operator.
* This added complexity in the access methods makes the planner a lot easier
* to write.
* 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.
* This added complexity in the access methods makes the planner a lot easier
* to write.
*/
/* if a op b, what operator tells us if (not a op b)? */
static StrategyNumber GISTNegate[GISTNStrategies] = {
InvalidStrategy,
InvalidStrategy,
InvalidStrategy
};
static StrategyNumber GISTNegate[GISTNStrategies] = {
InvalidStrategy,
InvalidStrategy,
InvalidStrategy
};
/* if a op_1 b, what is the operator op_2 such that b op_2 a? */
static StrategyNumber GISTCommute[GISTNStrategies] = {
InvalidStrategy,
InvalidStrategy,
InvalidStrategy
};
static StrategyNumber GISTCommute[GISTNStrategies] = {
InvalidStrategy,
InvalidStrategy,
InvalidStrategy
};
/* if a op_1 b, what is the operator op_2 such that (b !op_2 a)? */
static StrategyNumber GISTNegateCommute[GISTNStrategies] = {
InvalidStrategy,
InvalidStrategy,
InvalidStrategy
};
static StrategyNumber GISTNegateCommute[GISTNStrategies] = {
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
* discussion of
* TermData) -- such logic must be encoded in the user's Consistent function.
*/
/*
* If you were sufficiently attentive to detail, you would go through
* the ExpressionData pain above for every one of the 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.
* If you were sufficiently attentive to detail, you would go through
* the ExpressionData pain above for every one of the 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.
* 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 GISTEvaluationData = {
GISTNStrategies, /* # of strategies */
(StrategyTransformMap) GISTNegate, /* how to do (not qual) */
(StrategyTransformMap) GISTCommute, /* how to swap operands */
(StrategyTransformMap) GISTNegateCommute, /* how to do both */
{ NULL }
GISTNStrategies, /* # of strategies */
(StrategyTransformMap) GISTNegate, /* how to do (not qual) */
(StrategyTransformMap) GISTCommute, /* how to swap operands */
(StrategyTransformMap) GISTNegateCommute, /* how to do both */
{NULL}
};
StrategyNumber
RelationGetGISTStrategy(Relation r,
AttrNumber attnum,
RegProcedure proc)
AttrNumber attnum,
RegProcedure proc)
{
return (RelationGetStrategy(r, attnum, &GISTEvaluationData, proc));
return (RelationGetStrategy(r, attnum, &GISTEvaluationData, proc));
}
#ifdef NOT_USED
bool
RelationInvokeGISTStrategy(Relation r,
AttrNumber attnum,
StrategyNumber s,
Datum left,
Datum right)
AttrNumber attnum,
StrategyNumber s,
Datum left,
Datum right)
{
return (RelationInvokeStrategy(r, &GISTEvaluationData, attnum, s,
left, right));
return (RelationInvokeStrategy(r, &GISTEvaluationData, attnum, s,
left, right));
}
#endif

View File

@ -1,16 +1,16 @@
/*-------------------------------------------------------------------------
*
* 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
*
*
* 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
* This file contains only the public interface routines.
* This file contains only the public interface routines.
*
*-------------------------------------------------------------------------
*/
@ -26,452 +26,483 @@
#include <miscadmin.h>
#ifndef HAVE_MEMMOVE
# include <regex/utils.h>
#include <regex/utils.h>
#else
# include <string.h>
#include <string.h>
#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
* a new index. This is used to avoid high-concurrency locking,
* since the index won't be visible until this transaction commits
* and since building is guaranteed to be single-threaded.
* We use a global variable to record the fact that we're creating
* a new index. This is used to avoid high-concurrency locking,
* since the index won't be visible until this transaction commits
* and since building is guaranteed to be single-threaded.
*/
void
hashbuild(Relation heap,
Relation index,
int natts,
AttrNumber *attnum,
IndexStrategy istrat,
uint16 pcount,
Datum *params,
FuncIndexInfo *finfo,
PredInfo *predInfo)
Relation index,
int natts,
AttrNumber * attnum,
IndexStrategy istrat,
uint16 pcount,
Datum * params,
FuncIndexInfo * finfo,
PredInfo * predInfo)
{
HeapScanDesc hscan;
Buffer buffer;
HeapTuple htup;
IndexTuple itup;
TupleDesc htupdesc, itupdesc;
Datum *attdata;
bool *nulls;
InsertIndexResult res;
int nhtups, nitups;
int i;
HashItem hitem;
HeapScanDesc hscan;
Buffer buffer;
HeapTuple htup;
IndexTuple itup;
TupleDesc htupdesc,
itupdesc;
Datum *attdata;
bool *nulls;
InsertIndexResult res;
int nhtups,
nitups;
int i;
HashItem hitem;
#ifndef OMIT_PARTIAL_INDEX
ExprContext *econtext;
TupleTable tupleTable;
TupleTableSlot *slot;
ExprContext *econtext;
TupleTable tupleTable;
TupleTableSlot *slot;
#endif
Oid hrelid, irelid;
Node *pred, *oldPred;
/* note that this is a new btree */
BuildingHash = true;
pred = predInfo->pred;
oldPred = predInfo->oldPred;
/* initialize the hash index metadata page (if this is a new index) */
if (oldPred == NULL)
_hash_metapinit(index);
/* get tuple descriptors for heap and index relations */
htupdesc = RelationGetTupleDescriptor(heap);
itupdesc = RelationGetTupleDescriptor(index);
/* get space for data items that'll appear in the index tuple */
attdata = (Datum *) palloc(natts * sizeof(Datum));
nulls = (bool *) palloc(natts * sizeof(bool));
/*
* If this is a predicate (partial) index, we will need to evaluate the
* predicate using ExecQual, which requires the current tuple to be in a
* slot of a TupleTable. In addition, ExecQual must have an ExprContext
* referring to that slot. Here, we initialize dummy TupleTable and
* ExprContext objects for this purpose. --Nels, Feb '92
*/
Oid hrelid,
irelid;
Node *pred,
*oldPred;
/* note that this is a new btree */
BuildingHash = true;
pred = predInfo->pred;
oldPred = predInfo->oldPred;
/* initialize the hash index metadata page (if this is a new index) */
if (oldPred == NULL)
_hash_metapinit(index);
/* get tuple descriptors for heap and index relations */
htupdesc = RelationGetTupleDescriptor(heap);
itupdesc = RelationGetTupleDescriptor(index);
/* get space for data items that'll appear in the index tuple */
attdata = (Datum *) palloc(natts * sizeof(Datum));
nulls = (bool *) palloc(natts * sizeof(bool));
/*
* If this is a predicate (partial) index, we will need to evaluate
* the predicate using ExecQual, which requires the current tuple to
* be in a slot of a TupleTable. In addition, ExecQual must have an
* ExprContext referring to that slot. Here, we initialize dummy
* TupleTable and ExprContext objects for this purpose. --Nels, Feb
* '92
*/
#ifndef OMIT_PARTIAL_INDEX
if (pred != NULL || oldPred != NULL) {
tupleTable = ExecCreateTupleTable(1);
slot = ExecAllocTableSlot(tupleTable);
econtext = makeNode(ExprContext);
FillDummyExprContext(econtext, slot, htupdesc, buffer);
}
else /* quiet the compiler */
if (pred != NULL || oldPred != NULL)
{
tupleTable = ExecCreateTupleTable(1);
slot = ExecAllocTableSlot(tupleTable);
econtext = makeNode(ExprContext);
FillDummyExprContext(econtext, slot, htupdesc, buffer);
}
else
/* quiet the compiler */
{
econtext = NULL;
tupleTable = 0;
slot = 0;
}
#endif /* OMIT_PARTIAL_INDEX */
/* start a heap scan */
hscan = heap_beginscan(heap, 0, NowTimeQual, 0, (ScanKey) NULL);
htup = heap_getnext(hscan, 0, &buffer);
/* build the index */
nhtups = nitups = 0;
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())
#endif /* OMIT_PARTIAL_INDEX */
/* start a heap scan */
hscan = heap_beginscan(heap, 0, NowTimeQual, 0, (ScanKey) NULL);
htup = heap_getnext(hscan, 0, &buffer);
/* build the index */
nhtups = nitups = 0;
for (; HeapTupleIsValid(htup); htup = heap_getnext(hscan, 0, &buffer))
{
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);
}
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);
}
/* be tidy */
pfree(nulls);
pfree(attdata);
/* all done */
BuildingHash = false;
/* 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;
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
* for the new tuple, put it there, and return an InsertIndexResult
* to the caller.
* Hash on the index tuple's key, find the appropriate location
* for the new tuple, put it there, and return an InsertIndexResult
* to the caller.
*/
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;
IndexTuple itup;
InsertIndexResult res;
HashItem hitem;
IndexTuple itup;
InsertIndexResult res;
/* generate an index tuple */
itup = index_formtuple(RelationGetTupleDescriptor(rel), datum, nulls);
itup->t_tid = *ht_ctid;
if (itup->t_info & INDEX_NULL_MASK)
return ((InsertIndexResult) NULL);
hitem = _hash_formitem(itup);
res = _hash_doinsert(rel, hitem);
pfree(hitem);
pfree(itup);
return (res);
/* generate an index tuple */
itup = index_formtuple(RelationGetTupleDescriptor(rel), datum, nulls);
itup->t_tid = *ht_ctid;
if (itup->t_info & INDEX_NULL_MASK)
return ((InsertIndexResult) NULL);
hitem = _hash_formitem(itup);
res = _hash_doinsert(rel, hitem);
pfree(hitem);
pfree(itup);
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)
{
RetrieveIndexResult res;
/*
* If we've already initialized this scan, we can just advance it
* in the appropriate direction. If we haven't done so yet, we
* call a routine to get the first item in the scan.
*/
if (ItemPointerIsValid(&(scan->currentItemData)))
res = _hash_next(scan, dir);
else
res = _hash_first(scan, dir);
return ((char *) res);
RetrieveIndexResult res;
/*
* If we've already initialized this scan, we can just advance it in
* the appropriate direction. If we haven't done so yet, we call a
* routine to get the first item in the scan.
*/
if (ItemPointerIsValid(&(scan->currentItemData)))
res = _hash_next(scan, dir);
else
res = _hash_first(scan, dir);
return ((char *) res);
}
/*
* hashbeginscan() -- start a scan on a hash index
* hashbeginscan() -- start a scan on a hash index
*/
char *
char *
hashbeginscan(Relation rel,
bool fromEnd,
uint16 keysz,
ScanKey scankey)
bool fromEnd,
uint16 keysz,
ScanKey scankey)
{
IndexScanDesc scan;
HashScanOpaque so;
scan = RelationGetIndexScan(rel, fromEnd, keysz, scankey);
so = (HashScanOpaque) palloc(sizeof(HashScanOpaqueData));
so->hashso_curbuf = so->hashso_mrkbuf = InvalidBuffer;
scan->opaque = so;
scan->flags = 0x0;
/* register scan in case we change pages it's using */
_hash_regscan(scan);
return ((char *) scan);
IndexScanDesc scan;
HashScanOpaque so;
scan = RelationGetIndexScan(rel, fromEnd, keysz, scankey);
so = (HashScanOpaque) palloc(sizeof(HashScanOpaqueData));
so->hashso_curbuf = so->hashso_mrkbuf = InvalidBuffer;
scan->opaque = so;
scan->flags = 0x0;
/* register scan in case we change pages it's using */
_hash_regscan(scan);
return ((char *) scan);
}
/*
* hashrescan() -- rescan an index relation
* hashrescan() -- rescan an index relation
*/
void
hashrescan(IndexScanDesc scan, bool fromEnd, ScanKey scankey)
{
ItemPointer iptr;
HashScanOpaque so;
so = (HashScanOpaque) scan->opaque;
/* we hold a read lock on the current page in the scan */
if (ItemPointerIsValid(iptr = &(scan->currentItemData))) {
_hash_relbuf(scan->relation, so->hashso_curbuf, HASH_READ);
so->hashso_curbuf = InvalidBuffer;
ItemPointerSetInvalid(iptr);
}
if (ItemPointerIsValid(iptr = &(scan->currentMarkData))) {
_hash_relbuf(scan->relation, so->hashso_mrkbuf, HASH_READ);
so->hashso_mrkbuf = InvalidBuffer;
ItemPointerSetInvalid(iptr);
}
/* reset the scan key */
if (scan->numberOfKeys > 0) {
memmove(scan->keyData,
scankey,
scan->numberOfKeys * sizeof(ScanKeyData));
}
ItemPointer iptr;
HashScanOpaque so;
so = (HashScanOpaque) scan->opaque;
/* we hold a read lock on the current page in the scan */
if (ItemPointerIsValid(iptr = &(scan->currentItemData)))
{
_hash_relbuf(scan->relation, so->hashso_curbuf, HASH_READ);
so->hashso_curbuf = InvalidBuffer;
ItemPointerSetInvalid(iptr);
}
if (ItemPointerIsValid(iptr = &(scan->currentMarkData)))
{
_hash_relbuf(scan->relation, so->hashso_mrkbuf, HASH_READ);
so->hashso_mrkbuf = InvalidBuffer;
ItemPointerSetInvalid(iptr);
}
/* reset the scan key */
if (scan->numberOfKeys > 0)
{
memmove(scan->keyData,
scankey,
scan->numberOfKeys * sizeof(ScanKeyData));
}
}
/*
* hashendscan() -- close down a scan
* hashendscan() -- close down a scan
*/
void
hashendscan(IndexScanDesc scan)
{
ItemPointer iptr;
HashScanOpaque so;
so = (HashScanOpaque) scan->opaque;
/* release any locks we still hold */
if (ItemPointerIsValid(iptr = &(scan->currentItemData))) {
_hash_relbuf(scan->relation, so->hashso_curbuf, HASH_READ);
so->hashso_curbuf = InvalidBuffer;
ItemPointerSetInvalid(iptr);
}
if (ItemPointerIsValid(iptr = &(scan->currentMarkData))) {
if (BufferIsValid(so->hashso_mrkbuf))
_hash_relbuf(scan->relation, so->hashso_mrkbuf, HASH_READ);
so->hashso_mrkbuf = InvalidBuffer;
ItemPointerSetInvalid(iptr);
}
/* don't need scan registered anymore */
_hash_dropscan(scan);
/* be tidy */
pfree (scan->opaque);
ItemPointer iptr;
HashScanOpaque so;
so = (HashScanOpaque) scan->opaque;
/* release any locks we still hold */
if (ItemPointerIsValid(iptr = &(scan->currentItemData)))
{
_hash_relbuf(scan->relation, so->hashso_curbuf, HASH_READ);
so->hashso_curbuf = InvalidBuffer;
ItemPointerSetInvalid(iptr);
}
if (ItemPointerIsValid(iptr = &(scan->currentMarkData)))
{
if (BufferIsValid(so->hashso_mrkbuf))
_hash_relbuf(scan->relation, so->hashso_mrkbuf, HASH_READ);
so->hashso_mrkbuf = InvalidBuffer;
ItemPointerSetInvalid(iptr);
}
/* don't need scan registered anymore */
_hash_dropscan(scan);
/* be tidy */
pfree(scan->opaque);
}
/*
* hashmarkpos() -- save current scan position
* hashmarkpos() -- save current scan position
*
*/
void
hashmarkpos(IndexScanDesc scan)
{
ItemPointer iptr;
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
* is never called, so_mrkbuf should be removed from the scan->opaque
* structure.
*/
elog(NOTICE, "Hashmarkpos() called.");
so = (HashScanOpaque) scan->opaque;
/* release lock on old marked data, if any */
if (ItemPointerIsValid(iptr = &(scan->currentMarkData))) {
_hash_relbuf(scan->relation, so->hashso_mrkbuf, HASH_READ);
so->hashso_mrkbuf = InvalidBuffer;
ItemPointerSetInvalid(iptr);
}
/* bump lock on currentItemData and copy to currentMarkData */
if (ItemPointerIsValid(&(scan->currentItemData))) {
so->hashso_mrkbuf = _hash_getbuf(scan->relation,
BufferGetBlockNumber(so->hashso_curbuf),
HASH_READ);
scan->currentMarkData = scan->currentItemData;
}
ItemPointer iptr;
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 is never
* called, so_mrkbuf should be removed from the scan->opaque
* structure.
*/
elog(NOTICE, "Hashmarkpos() called.");
so = (HashScanOpaque) scan->opaque;
/* release lock on old marked data, if any */
if (ItemPointerIsValid(iptr = &(scan->currentMarkData)))
{
_hash_relbuf(scan->relation, so->hashso_mrkbuf, HASH_READ);
so->hashso_mrkbuf = InvalidBuffer;
ItemPointerSetInvalid(iptr);
}
/* bump lock on currentItemData and copy to currentMarkData */
if (ItemPointerIsValid(&(scan->currentItemData)))
{
so->hashso_mrkbuf = _hash_getbuf(scan->relation,
BufferGetBlockNumber(so->hashso_curbuf),
HASH_READ);
scan->currentMarkData = scan->currentItemData;
}
}
/*
* hashrestrpos() -- restore scan to last saved position
* hashrestrpos() -- restore scan to last saved position
*/
void
hashrestrpos(IndexScanDesc scan)
{
ItemPointer iptr;
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
* is never called, so_mrkbuf should be removed from the scan->opaque
* structure.
*/
elog(NOTICE, "Hashrestrpos() called.");
so = (HashScanOpaque) scan->opaque;
/* release lock on current data, if any */
if (ItemPointerIsValid(iptr = &(scan->currentItemData))) {
_hash_relbuf(scan->relation, so->hashso_curbuf, HASH_READ);
so->hashso_curbuf = InvalidBuffer;
ItemPointerSetInvalid(iptr);
}
/* bump lock on currentMarkData and copy to currentItemData */
if (ItemPointerIsValid(&(scan->currentMarkData))) {
so->hashso_curbuf =
_hash_getbuf(scan->relation,
BufferGetBlockNumber(so->hashso_mrkbuf),
HASH_READ);
scan->currentItemData = scan->currentMarkData;
}
ItemPointer iptr;
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 is never
* called, so_mrkbuf should be removed from the scan->opaque
* structure.
*/
elog(NOTICE, "Hashrestrpos() called.");
so = (HashScanOpaque) scan->opaque;
/* release lock on current data, if any */
if (ItemPointerIsValid(iptr = &(scan->currentItemData)))
{
_hash_relbuf(scan->relation, so->hashso_curbuf, HASH_READ);
so->hashso_curbuf = InvalidBuffer;
ItemPointerSetInvalid(iptr);
}
/* bump lock on currentMarkData and copy to currentItemData */
if (ItemPointerIsValid(&(scan->currentMarkData)))
{
so->hashso_curbuf =
_hash_getbuf(scan->relation,
BufferGetBlockNumber(so->hashso_mrkbuf),
HASH_READ);
scan->currentItemData = scan->currentMarkData;
}
}
/* stubs */
void
hashdelete(Relation rel, ItemPointer tid)
{
/* adjust any active scans that will be affected by this deletion */
_hash_adjscans(rel, tid);
/* delete the data from the page */
_hash_pagedel(rel, tid);
}
/* adjust any active scans that will be affected by this deletion */
_hash_adjscans(rel, tid);
/* delete the data from the page */
_hash_pagedel(rel, tid);
}

View File

@ -1,17 +1,17 @@
/*-------------------------------------------------------------------------
*
* hashfunc.c--
* Comparison functions for hash access method.
* Comparison functions for hash access method.
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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
* These functions are stored in pg_amproc. For each operator class
* defined on hash tables, they compute the hash value of the argument.
* These functions are stored in pg_amproc. For each operator class
* defined on hash tables, they compute the hash value of the argument.
*
*-------------------------------------------------------------------------
*/
@ -20,206 +20,223 @@
#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. */
uint32 hashfloat4(float32 keyp)
uint32
hashfloat4(float32 keyp)
{
int len;
int loop;
uint32 h;
char *kp = (char *) keyp;
int len;
int loop;
uint32 h;
char *kp = (char *) keyp;
len = sizeof(float32data);
len = sizeof(float32data);
#define HASH4a h = (h << 5) - h + *kp++;
#define HASH4b h = (h << 5) + h + *kp++;
#define HASH4a h = (h << 5) - h + *kp++;
#define HASH4b h = (h << 5) + h + *kp++;
#define HASH4 HASH4b
h = 0;
if (len > 0) {
loop = (len + 8 - 1) >> 3;
switch (len & (8 - 1)) {
case 0:
do { /* All fall throughs */
HASH4;
case 7:
HASH4;
case 6:
HASH4;
case 5:
HASH4;
case 4:
HASH4;
case 3:
HASH4;
case 2:
HASH4;
case 1:
HASH4;
} while (--loop);
h = 0;
if (len > 0)
{
loop = (len + 8 - 1) >> 3;
switch (len & (8 - 1))
{
case 0:
do
{ /* All fall throughs */
HASH4;
case 7:
HASH4;
case 6:
HASH4;
case 5:
HASH4;
case 4:
HASH4;
case 3:
HASH4;
case 2:
HASH4;
case 1:
HASH4;
} while (--loop);
}
}
}
return (h);
}
return (h);
}
uint32 hashfloat8(float64 keyp)
uint32
hashfloat8(float64 keyp)
{
int len;
int loop;
uint32 h;
char *kp = (char *) keyp;
int len;
int loop;
uint32 h;
char *kp = (char *) keyp;
len = sizeof(float64data);
len = sizeof(float64data);
#define HASH4a h = (h << 5) - h + *kp++;
#define HASH4b h = (h << 5) + h + *kp++;
#define HASH4a h = (h << 5) - h + *kp++;
#define HASH4b h = (h << 5) + h + *kp++;
#define HASH4 HASH4b
h = 0;
if (len > 0) {
loop = (len + 8 - 1) >> 3;
switch (len & (8 - 1)) {
case 0:
do { /* All fall throughs */
HASH4;
case 7:
HASH4;
case 6:
HASH4;
case 5:
HASH4;
case 4:
HASH4;
case 3:
HASH4;
case 2:
HASH4;
case 1:
HASH4;
} while (--loop);
h = 0;
if (len > 0)
{
loop = (len + 8 - 1) >> 3;
switch (len & (8 - 1))
{
case 0:
do
{ /* All fall throughs */
HASH4;
case 7:
HASH4;
case 6:
HASH4;
case 5:
HASH4;
case 4:
HASH4;
case 3:
HASH4;
case 2:
HASH4;
case 1:
HASH4;
} while (--loop);
}
}
}
return (h);
}
uint32 hashoid(Oid key)
{
return ((uint32) ~key);
return (h);
}
uint32 hashchar(char key)
uint32
hashoid(Oid key)
{
int len;
uint32 h;
len = sizeof(char);
#define PRIME1 37
#define PRIME2 1048583
h = 0;
/* Convert char to integer */
h = h * PRIME1 ^ (key - ' ');
h %= PRIME2;
return (h);
}
uint32 hashchar2(uint16 intkey)
{
uint32 h;
int len;
char *key = (char *) &intkey;
h = 0;
len = sizeof(uint16);
/* Convert string to integer */
while (len--)
h = h * PRIME1 ^ (*key++ - ' ');
h %= PRIME2;
return (h);
}
uint32 hashchar4(uint32 intkey)
{
uint32 h;
int len;
char *key = (char *) &intkey;
h = 0;
len = sizeof(uint32);
/* Convert string to integer */
while (len--)
h = h * PRIME1 ^ (*key++ - ' ');
h %= PRIME2;
return (h);
}
uint32 hashchar8(char *key)
{
uint32 h;
int len;
h = 0;
len = sizeof(char8);
/* Convert string to integer */
while (len--)
h = h * PRIME1 ^ (*key++ - ' ');
h %= PRIME2;
return (h);
}
uint32 hashname(NameData *n)
{
uint32 h;
int len;
char *key;
key = n->data;
h = 0;
len = NAMEDATALEN;
/* Convert string to integer */
while (len--)
h = h * PRIME1 ^ (*key++ - ' ');
h %= PRIME2;
return (h);
return ((uint32) ~ key);
}
uint32 hashchar16(char *key)
uint32
hashchar(char key)
{
uint32 h;
int len;
h = 0;
len = sizeof(char16);
/* Convert string to integer */
while (len--)
h = h * PRIME1 ^ (*key++ - ' ');
h %= PRIME2;
return (h);
int len;
uint32 h;
len = sizeof(char);
#define PRIME1 37
#define PRIME2 1048583
h = 0;
/* Convert char to integer */
h = h * PRIME1 ^ (key - ' ');
h %= PRIME2;
return (h);
}
uint32
hashchar2(uint16 intkey)
{
uint32 h;
int len;
char *key = (char *) &intkey;
h = 0;
len = sizeof(uint16);
/* Convert string to integer */
while (len--)
h = h * PRIME1 ^ (*key++ - ' ');
h %= PRIME2;
return (h);
}
uint32
hashchar4(uint32 intkey)
{
uint32 h;
int len;
char *key = (char *) &intkey;
h = 0;
len = sizeof(uint32);
/* Convert string to integer */
while (len--)
h = h * PRIME1 ^ (*key++ - ' ');
h %= PRIME2;
return (h);
}
uint32
hashchar8(char *key)
{
uint32 h;
int len;
h = 0;
len = sizeof(char8);
/* Convert string to integer */
while (len--)
h = h * PRIME1 ^ (*key++ - ' ');
h %= PRIME2;
return (h);
}
uint32
hashname(NameData * n)
{
uint32 h;
int len;
char *key;
key = n->data;
h = 0;
len = NAMEDATALEN;
/* Convert string to integer */
while (len--)
h = h * PRIME1 ^ (*key++ - ' ');
h %= PRIME2;
return (h);
}
uint32
hashchar16(char *key)
{
uint32 h;
int len;
h = 0;
len = sizeof(char16);
/* Convert string to integer */
while (len--)
h = h * PRIME1 ^ (*key++ - ' ');
h %= PRIME2;
return (h);
}
@ -234,45 +251,49 @@ uint32 hashchar16(char *key)
*
* "OZ's original sdbm hash"
*/
uint32 hashtext(struct varlena *key)
uint32
hashtext(struct varlena * key)
{
int keylen;
char *keydata;
uint32 n;
int loop;
int keylen;
char *keydata;
uint32 n;
int loop;
keydata = VARDATA(key);
keylen = VARSIZE(key);
keydata = VARDATA(key);
keylen = VARSIZE(key);
/* keylen includes the four bytes in which string keylength is stored */
keylen -= sizeof(VARSIZE(key));
/* keylen includes the four bytes in which string keylength is stored */
keylen -= sizeof(VARSIZE(key));
#define HASHC n = *keydata++ + 65599 * n
#define HASHC n = *keydata++ + 65599 * n
n = 0;
if (keylen > 0) {
loop = (keylen + 8 - 1) >> 3;
switch (keylen & (8 - 1)) {
case 0:
do { /* All fall throughs */
HASHC;
case 7:
HASHC;
case 6:
HASHC;
case 5:
HASHC;
case 4:
HASHC;
case 3:
HASHC;
case 2:
HASHC;
case 1:
HASHC;
} while (--loop);
n = 0;
if (keylen > 0)
{
loop = (keylen + 8 - 1) >> 3;
switch (keylen & (8 - 1))
{
case 0:
do
{ /* All fall throughs */
HASHC;
case 7:
HASHC;
case 6:
HASHC;
case 5:
HASHC;
case 4:
HASHC;
case 3:
HASHC;
case 2:
HASHC;
case 1:
HASHC;
} while (--loop);
}
}
}
return (n);
}
return (n);
}

View File

@ -1,19 +1,19 @@
/*-------------------------------------------------------------------------
*
* 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
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#include <postgres.h>
#include <access/hash.h>
#include <storage/bufmgr.h>
#include <utils/memutils.h>
@ -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);
/*
* _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
* 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
* hashitem.
* This routine is called by the public interface routines, hashbuild
* 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
* hashitem.
*/
InsertIndexResult
_hash_doinsert(Relation rel, HashItem hitem)
{
Buffer buf;
Buffer metabuf;
BlockNumber blkno;
HashMetaPage metap;
IndexTuple itup;
InsertIndexResult res;
ScanKey itup_scankey;
int natts;
Page page;
metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ);
metap = (HashMetaPage) BufferGetPage(metabuf);
_hash_checkpage((Page) metap, LH_META_PAGE);
/* we need a scan key to do our search, so build one */
itup = &(hitem->hash_itup);
if ((natts = rel->rd_rel->relnatts) != 1)
elog(WARN, "Hash indices valid for only one index key.");
itup_scankey = _hash_mkscankey(rel, itup, metap);
/*
* find the first page in the bucket chain containing this key and
* place it in buf. _hash_search obtains a read lock for us.
*/
_hash_search(rel, natts, itup_scankey, &buf, metap);
page = BufferGetPage(buf);
_hash_checkpage(page, LH_BUCKET_PAGE);
Buffer buf;
Buffer metabuf;
BlockNumber blkno;
HashMetaPage metap;
IndexTuple itup;
InsertIndexResult res;
ScanKey itup_scankey;
int natts;
Page page;
/*
* trade in our read lock for a write lock so that we can do the
* insertion.
*/
blkno = BufferGetBlockNumber(buf);
_hash_relbuf(rel, buf, HASH_READ);
buf = _hash_getbuf(rel, blkno, HASH_WRITE);
/*
* XXX btree comment (haven't decided what to do in hash): don't
* think the bucket can be split while we're reading the metapage.
*
* If the page was split between the time that we surrendered our
* read lock and acquired our write lock, then this page may no
* longer be the right place for the key we want to insert.
*/
/* do the insertion */
res = _hash_insertonpg(rel, buf, natts, itup_scankey,
hitem, metabuf);
/* be tidy */
_hash_freeskey(itup_scankey);
return (res);
metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ);
metap = (HashMetaPage) BufferGetPage(metabuf);
_hash_checkpage((Page) metap, LH_META_PAGE);
/* we need a scan key to do our search, so build one */
itup = &(hitem->hash_itup);
if ((natts = rel->rd_rel->relnatts) != 1)
elog(WARN, "Hash indices valid for only one index key.");
itup_scankey = _hash_mkscankey(rel, itup, metap);
/*
* find the first page in the bucket chain containing this key and
* place it in buf. _hash_search obtains a read lock for us.
*/
_hash_search(rel, natts, itup_scankey, &buf, metap);
page = BufferGetPage(buf);
_hash_checkpage(page, LH_BUCKET_PAGE);
/*
* trade in our read lock for a write lock so that we can do the
* insertion.
*/
blkno = BufferGetBlockNumber(buf);
_hash_relbuf(rel, buf, HASH_READ);
buf = _hash_getbuf(rel, blkno, HASH_WRITE);
/*
* XXX btree comment (haven't decided what to do in hash): don't think
* the bucket can be split while we're reading the metapage.
*
* If the page was split between the time that we surrendered our read
* lock and acquired our write lock, then this page may no longer be
* the right place for the key we want to insert.
*/
/* do the insertion */
res = _hash_insertonpg(rel, buf, natts, itup_scankey,
hitem, metabuf);
/* be tidy */
_hash_freeskey(itup_scankey);
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.
* + inserts the tuple.
* + if necessary, splits the target page.
* + inserts the tuple.
*
* On entry, we must have the right buffer on which to do the
* 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.
* On entry, we must have the right buffer on which to do the
* 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.
*
*/
static InsertIndexResult
static InsertIndexResult
_hash_insertonpg(Relation rel,
Buffer buf,
int keysz,
ScanKey scankey,
HashItem hitem,
Buffer metabuf)
Buffer buf,
int keysz,
ScanKey scankey,
HashItem hitem,
Buffer metabuf)
{
InsertIndexResult res;
Page page;
BlockNumber itup_blkno;
OffsetNumber itup_off;
int itemsz;
HashPageOpaque pageopaque;
bool do_expand = false;
Buffer ovflbuf;
HashMetaPage metap;
Bucket bucket;
metap = (HashMetaPage) BufferGetPage(metabuf);
_hash_checkpage((Page) metap, LH_META_PAGE);
page = BufferGetPage(buf);
_hash_checkpage(page, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE);
pageopaque = (HashPageOpaque) PageGetSpecialPointer(page);
bucket = pageopaque->hasho_bucket;
InsertIndexResult res;
Page page;
BlockNumber itup_blkno;
OffsetNumber itup_off;
int itemsz;
HashPageOpaque pageopaque;
bool do_expand = false;
Buffer ovflbuf;
HashMetaPage metap;
Bucket 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);
metap = (HashMetaPage) BufferGetPage(metabuf);
_hash_checkpage((Page) metap, LH_META_PAGE);
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);
page = BufferGetPage(buf);
_hash_checkpage(page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
pageopaque = (HashPageOpaque) PageGetSpecialPointer(page);
Assert(pageopaque->hasho_bucket == bucket);
}
bucket = pageopaque->hasho_bucket;
itup_off = _hash_pgaddtup(rel, buf, keysz, scankey, itemsz, hitem);
itup_blkno = BufferGetBlockNumber(buf);
/* by here, the new tuple is inserted */
res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData));
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);
}
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);
Assert(pageopaque->hasho_bucket == bucket);
}
itup_off = _hash_pgaddtup(rel, buf, keysz, scankey, itemsz, hitem);
itup_blkno = BufferGetBlockNumber(buf);
/* by here, the new tuple is inserted */
res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData));
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
* write lock and reference associated with the page's buffer. It is
* an error to call pgaddtup() without a write lock and reference.
* 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
* an error to call pgaddtup() without a write lock and reference.
*/
static OffsetNumber
static OffsetNumber
_hash_pgaddtup(Relation rel,
Buffer buf,
int keysz,
ScanKey itup_scankey,
Size itemsize,
HashItem hitem)
Buffer buf,
int keysz,
ScanKey itup_scankey,
Size itemsize,
HashItem hitem)
{
OffsetNumber itup_off;
Page page;
page = BufferGetPage(buf);
_hash_checkpage(page, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE);
OffsetNumber itup_off;
Page page;
itup_off = OffsetNumberNext(PageGetMaxOffsetNumber(page));
PageAddItem(page, (Item) hitem, itemsize, itup_off, LP_USED);
/* write the buffer, but hold our lock */
_hash_wrtnorelbuf(rel, buf);
return (itup_off);
page = BufferGetPage(buf);
_hash_checkpage(page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
itup_off = OffsetNumberNext(PageGetMaxOffsetNumber(page));
PageAddItem(page, (Item) hitem, itemsize, itup_off, LP_USED);
/* write the buffer, but hold our lock */
_hash_wrtnorelbuf(rel, buf);
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,160 +1,167 @@
/*-------------------------------------------------------------------------
*
* hashscan.c--
* manage scans on hash tables
* manage scans on hash tables
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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
* 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. The routines and global variables in this file
* guarantee that all scans in the local address space stay
* correctly positioned. This 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.
* 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. The routines and global variables in this file
* guarantee that all scans in the local address space stay
* correctly positioned. This 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.
*
* The scheme is to manage a list of active scans in the current
* backend. Whenever we add or remove records from an index, 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 relation, and the
* same page, as the update.
* The scheme is to manage a list of active scans in the current
* backend. Whenever we add or remove records from an index, 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 relation, and the
* same page, as the update.
*
*-------------------------------------------------------------------------
*/
#include <postgres.h>
#include <access/hash.h>
static void _hash_scandel(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno);
static bool _hash_scantouched(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);
typedef struct HashScanListData {
IndexScanDesc hashsl_scan;
struct HashScanListData *hashsl_next;
} HashScanListData;
typedef struct HashScanListData
{
IndexScanDesc hashsl_scan;
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
_hash_regscan(IndexScanDesc scan)
{
HashScanList new_el;
new_el = (HashScanList) palloc(sizeof(HashScanListData));
new_el->hashsl_scan = scan;
new_el->hashsl_next = HashScans;
HashScans = new_el;
HashScanList new_el;
new_el = (HashScanList) palloc(sizeof(HashScanListData));
new_el->hashsl_scan = scan;
new_el->hashsl_next = HashScans;
HashScans = new_el;
}
/*
* _hash_dropscan() -- drop a scan from the scan list
* _hash_dropscan() -- drop a scan from the scan list
*/
void
_hash_dropscan(IndexScanDesc scan)
{
HashScanList chk, last;
last = (HashScanList) NULL;
for (chk = HashScans;
chk != (HashScanList) NULL && chk->hashsl_scan != scan;
chk = chk->hashsl_next) {
last = chk;
}
if (chk == (HashScanList) NULL)
elog(WARN, "hash scan list trashed; can't find 0x%lx", scan);
if (last == (HashScanList) NULL)
HashScans = chk->hashsl_next;
else
last->hashsl_next = chk->hashsl_next;
pfree (chk);
HashScanList chk,
last;
last = (HashScanList) NULL;
for (chk = HashScans;
chk != (HashScanList) NULL && chk->hashsl_scan != scan;
chk = chk->hashsl_next)
{
last = chk;
}
if (chk == (HashScanList) NULL)
elog(WARN, "hash scan list trashed; can't find 0x%lx", scan);
if (last == (HashScanList) NULL)
HashScans = chk->hashsl_next;
else
last->hashsl_next = chk->hashsl_next;
pfree(chk);
}
void
_hash_adjscans(Relation rel, ItemPointer tid)
{
HashScanList l;
Oid relid;
relid = rel->rd_id;
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),
ItemPointerGetOffsetNumber(tid));
}
HashScanList l;
Oid relid;
relid = rel->rd_id;
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),
ItemPointerGetOffsetNumber(tid));
}
}
static void
_hash_scandel(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno)
{
ItemPointer current;
Buffer buf;
Buffer metabuf;
HashScanOpaque so;
if (!_hash_scantouched(scan, blkno, offno))
return;
metabuf = _hash_getbuf(scan->relation, HASH_METAPAGE, HASH_READ);
so = (HashScanOpaque) scan->opaque;
buf = so->hashso_curbuf;
current = &(scan->currentItemData);
if (ItemPointerIsValid(current)
&& ItemPointerGetBlockNumber(current) == blkno
&& ItemPointerGetOffsetNumber(current) >= offno) {
_hash_step(scan, &buf, BackwardScanDirection, metabuf);
so->hashso_curbuf = buf;
}
current = &(scan->currentMarkData);
if (ItemPointerIsValid(current)
&& ItemPointerGetBlockNumber(current) == blkno
&& ItemPointerGetOffsetNumber(current) >= offno) {
ItemPointerData tmp;
tmp = *current;
*current = scan->currentItemData;
scan->currentItemData = tmp;
_hash_step(scan, &buf, BackwardScanDirection, metabuf);
so->hashso_mrkbuf = buf;
tmp = *current;
*current = scan->currentItemData;
scan->currentItemData = tmp;
}
ItemPointer current;
Buffer buf;
Buffer metabuf;
HashScanOpaque so;
if (!_hash_scantouched(scan, blkno, offno))
return;
metabuf = _hash_getbuf(scan->relation, HASH_METAPAGE, HASH_READ);
so = (HashScanOpaque) scan->opaque;
buf = so->hashso_curbuf;
current = &(scan->currentItemData);
if (ItemPointerIsValid(current)
&& ItemPointerGetBlockNumber(current) == blkno
&& ItemPointerGetOffsetNumber(current) >= offno)
{
_hash_step(scan, &buf, BackwardScanDirection, metabuf);
so->hashso_curbuf = buf;
}
current = &(scan->currentMarkData);
if (ItemPointerIsValid(current)
&& ItemPointerGetBlockNumber(current) == blkno
&& ItemPointerGetOffsetNumber(current) >= offno)
{
ItemPointerData tmp;
tmp = *current;
*current = scan->currentItemData;
scan->currentItemData = tmp;
_hash_step(scan, &buf, BackwardScanDirection, metabuf);
so->hashso_mrkbuf = buf;
tmp = *current;
*current = scan->currentItemData;
scan->currentItemData = tmp;
}
}
static bool
static bool
_hash_scantouched(IndexScanDesc scan,
BlockNumber blkno,
OffsetNumber offno)
BlockNumber blkno,
OffsetNumber offno)
{
ItemPointer current;
current = &(scan->currentItemData);
if (ItemPointerIsValid(current)
&& ItemPointerGetBlockNumber(current) == blkno
&& ItemPointerGetOffsetNumber(current) >= offno)
return (true);
current = &(scan->currentMarkData);
if (ItemPointerIsValid(current)
&& ItemPointerGetBlockNumber(current) == blkno
&& ItemPointerGetOffsetNumber(current) >= offno)
return (true);
return (false);
ItemPointer current;
current = &(scan->currentItemData);
if (ItemPointerIsValid(current)
&& ItemPointerGetBlockNumber(current) == blkno
&& ItemPointerGetOffsetNumber(current) >= offno)
return (true);
current = &(scan->currentMarkData);
if (ItemPointerIsValid(current)
&& ItemPointerGetBlockNumber(current) == blkno
&& ItemPointerGetOffsetNumber(current) >= offno)
return (true);
return (false);
}

View File

@ -1,423 +1,467 @@
/*-------------------------------------------------------------------------
*
* hashsearch.c--
* search code for postgres hash tables
* search code for postgres hash tables
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#include <postgres.h>
#include <access/hash.h>
#include <storage/bufmgr.h>
#ifndef HAVE_MEMMOVE
# include "regex/utils.h"
#include "regex/utils.h"
#else
# include <string.h>
#endif
#include <string.h>
#endif
/*
* _hash_search() -- Finds the page/bucket that the contains the
* scankey and loads it into *bufP. the buffer has a read lock.
* _hash_search() -- Finds the page/bucket that the contains the
* scankey and loads it into *bufP. the buffer has a read lock.
*/
void
_hash_search(Relation rel,
int keysz,
ScanKey scankey,
Buffer *bufP,
HashMetaPage metap)
int keysz,
ScanKey scankey,
Buffer * bufP,
HashMetaPage metap)
{
BlockNumber blkno;
Datum keyDatum;
Bucket bucket;
BlockNumber blkno;
Datum keyDatum;
Bucket bucket;
if (scankey == (ScanKey) 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);
}
if (scankey == (ScanKey) NULL ||
(keyDatum = scankey[0].sk_argument) == (Datum) NULL)
{
blkno = BUCKET_TO_BLKNO(bucket);
*bufP = _hash_getbuf(rel, blkno, HASH_READ);
/*
* 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);
*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
* 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
* exit, we have the page containing the next item locked but not
* pinned.
* 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
* the page pinned. We return the next item in the scan. On
* exit, we have the page containing the next item locked but not
* pinned.
*/
RetrieveIndexResult
_hash_next(IndexScanDesc scan, ScanDirection dir)
{
Relation rel;
Buffer buf;
Buffer metabuf;
Page page;
OffsetNumber offnum;
RetrieveIndexResult res;
ItemPointer current;
HashItem hitem;
IndexTuple itup;
HashScanOpaque so;
Relation rel;
Buffer buf;
Buffer metabuf;
Page page;
OffsetNumber offnum;
RetrieveIndexResult res;
ItemPointer current;
HashItem hitem;
IndexTuple itup;
HashScanOpaque so;
rel = scan->relation;
so = (HashScanOpaque) scan->opaque;
current = &(scan->currentItemData);
rel = scan->relation;
so = (HashScanOpaque) scan->opaque;
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
* cached buffer for this scan. wei discovered it. the following
* is a workaround so he can work until i figure out what's going on.
*/
/*
* XXX 10 may 91: somewhere there's a bug in our management of the
* cached buffer for this scan. wei discovered it. the following is
* a workaround so he can work until i figure out what's going on.
*/
if (!BufferIsValid(so->hashso_curbuf)) {
so->hashso_curbuf = _hash_getbuf(rel,
ItemPointerGetBlockNumber(current),
HASH_READ);
}
if (!BufferIsValid(so->hashso_curbuf))
{
so->hashso_curbuf = _hash_getbuf(rel,
ItemPointerGetBlockNumber(current),
HASH_READ);
}
/* we still have the buffer pinned and locked */
buf = so->hashso_curbuf;
/* we still have the buffer pinned and locked */
buf = so->hashso_curbuf;
/*
* step to next valid tuple. note that _hash_step releases our
* lock on 'metabuf'; if we switch to a new 'buf' while looking
* for the next tuple, we come back with a lock on that buffer.
*/
if (!_hash_step(scan, &buf, dir, metabuf)) {
return ((RetrieveIndexResult) NULL);
}
/*
* step to next valid tuple. note that _hash_step releases our lock
* on 'metabuf'; if we switch to a new 'buf' while looking for the
* next tuple, we come back with a lock on that buffer.
*/
if (!_hash_step(scan, &buf, dir, metabuf))
{
return ((RetrieveIndexResult) NULL);
}
/* if we're here, _hash_step found a valid tuple */
current = &(scan->currentItemData);
offnum = ItemPointerGetOffsetNumber(current);
page = BufferGetPage(buf);
_hash_checkpage(page, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE);
hitem = (HashItem) PageGetItem(page, PageGetItemId(page, offnum));
itup = &hitem->hash_itup;
res = FormRetrieveIndexResult(current, &(itup->t_tid));
/* if we're here, _hash_step found a valid tuple */
current = &(scan->currentItemData);
offnum = ItemPointerGetOffsetNumber(current);
page = BufferGetPage(buf);
_hash_checkpage(page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
hitem = (HashItem) PageGetItem(page, PageGetItemId(page, offnum));
itup = &hitem->hash_itup;
res = FormRetrieveIndexResult(current, &(itup->t_tid));
return (res);
return (res);
}
static void
_hash_readnext(Relation rel,
Buffer *bufp, Page *pagep, HashPageOpaque *opaquep)
Buffer * bufp, Page * pagep, HashPageOpaque * opaquep)
{
BlockNumber blkno;
BlockNumber blkno;
blkno = (*opaquep)->hasho_nextblkno;
_hash_relbuf(rel, *bufp, HASH_READ);
*bufp = InvalidBuffer;
if (BlockNumberIsValid(blkno)) {
*bufp = _hash_getbuf(rel, blkno, HASH_READ);
*pagep = BufferGetPage(*bufp);
_hash_checkpage(*pagep, LH_OVERFLOW_PAGE);
*opaquep = (HashPageOpaque) PageGetSpecialPointer(*pagep);
Assert(!PageIsEmpty(*pagep));
}
blkno = (*opaquep)->hasho_nextblkno;
_hash_relbuf(rel, *bufp, HASH_READ);
*bufp = InvalidBuffer;
if (BlockNumberIsValid(blkno))
{
*bufp = _hash_getbuf(rel, blkno, HASH_READ);
*pagep = BufferGetPage(*bufp);
_hash_checkpage(*pagep, LH_OVERFLOW_PAGE);
*opaquep = (HashPageOpaque) PageGetSpecialPointer(*pagep);
Assert(!PageIsEmpty(*pagep));
}
}
static void
_hash_readprev(Relation rel,
Buffer *bufp, Page *pagep, HashPageOpaque *opaquep)
Buffer * bufp, Page * pagep, HashPageOpaque * opaquep)
{
BlockNumber blkno;
BlockNumber blkno;
blkno = (*opaquep)->hasho_prevblkno;
_hash_relbuf(rel, *bufp, HASH_READ);
*bufp = InvalidBuffer;
if (BlockNumberIsValid(blkno)) {
*bufp = _hash_getbuf(rel, blkno, HASH_READ);
*pagep = BufferGetPage(*bufp);
_hash_checkpage(*pagep, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE);
*opaquep = (HashPageOpaque) PageGetSpecialPointer(*pagep);
if (PageIsEmpty(*pagep)) {
Assert((*opaquep)->hasho_flag & LH_BUCKET_PAGE);
_hash_relbuf(rel, *bufp, HASH_READ);
*bufp = InvalidBuffer;
blkno = (*opaquep)->hasho_prevblkno;
_hash_relbuf(rel, *bufp, HASH_READ);
*bufp = InvalidBuffer;
if (BlockNumberIsValid(blkno))
{
*bufp = _hash_getbuf(rel, blkno, HASH_READ);
*pagep = BufferGetPage(*bufp);
_hash_checkpage(*pagep, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
*opaquep = (HashPageOpaque) PageGetSpecialPointer(*pagep);
if (PageIsEmpty(*pagep))
{
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
* satisfies the qualificatin associated with the scan descriptor. On
* exit, the page containing the current index tuple is read locked
* and pinned, and the scan's opaque data entry is updated to
* include the buffer.
* Return the RetrieveIndexResult of the first item in the tree that
* satisfies the qualificatin associated with the scan descriptor. On
* exit, the page containing the current index tuple is read locked
* and pinned, and the scan's opaque data entry is updated to
* include the buffer.
*/
RetrieveIndexResult
_hash_first(IndexScanDesc scan, ScanDirection dir)
{
Relation rel;
Buffer buf;
Buffer metabuf;
Page page;
HashPageOpaque opaque;
HashMetaPage metap;
HashItem hitem;
IndexTuple itup;
ItemPointer current;
OffsetNumber offnum;
RetrieveIndexResult res;
HashScanOpaque so;
Relation rel;
Buffer buf;
Buffer metabuf;
Page page;
HashPageOpaque opaque;
HashMetaPage metap;
HashItem hitem;
IndexTuple itup;
ItemPointer current;
OffsetNumber offnum;
RetrieveIndexResult res;
HashScanOpaque so;
rel = scan->relation;
so = (HashScanOpaque) scan->opaque;
current = &(scan->currentItemData);
rel = scan->relation;
so = (HashScanOpaque) scan->opaque;
current = &(scan->currentItemData);
metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ);
metap = (HashMetaPage) BufferGetPage(metabuf);
_hash_checkpage((Page) metap, LH_META_PAGE);
metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ);
metap = (HashMetaPage) BufferGetPage(metabuf);
_hash_checkpage((Page) metap, LH_META_PAGE);
/*
* XXX -- The attribute number stored in the scan key is the attno
* in the heap relation. We need to transmogrify this into
* the index relation attno here. For the moment, we have
* hardwired attno == 1.
*/
/*
* XXX -- The attribute number stored in the scan key is the attno in
* the heap relation. We need to transmogrify this into the index
* relation attno here. For the moment, we have hardwired attno == 1.
*/
/* find the correct bucket page and load it into buf */
_hash_search(rel, 1, scan->keyData, &buf, metap);
page = BufferGetPage(buf);
_hash_checkpage(page, LH_BUCKET_PAGE);
opaque = (HashPageOpaque) PageGetSpecialPointer(page);
/* find the correct bucket page and load it into buf */
_hash_search(rel, 1, scan->keyData, &buf, metap);
page = BufferGetPage(buf);
_hash_checkpage(page, LH_BUCKET_PAGE);
opaque = (HashPageOpaque) PageGetSpecialPointer(page);
/*
* if we are scanning forward, we need to find the first non-empty
* page (if any) in the bucket chain. since overflow pages are
* never empty, this had better be either the bucket page or the
* first overflow page.
*
* if we are scanning backward, we always go all the way to the
* end of the bucket chain.
*/
if (PageIsEmpty(page)) {
if (BlockNumberIsValid(opaque->hasho_nextblkno)) {
_hash_readnext(rel, &buf, &page, &opaque);
} else {
ItemPointerSetInvalid(current);
so->hashso_curbuf = InvalidBuffer;
/*
* If there is no scankeys, all tuples will satisfy
* the scan - so we continue in _hash_step to get
* tuples from all buckets. - vadim 04/29/97
*/
if ( scan->numberOfKeys >= 1 )
{
_hash_relbuf(rel, buf, HASH_READ);
_hash_relbuf(rel, metabuf, HASH_READ);
return ((RetrieveIndexResult) NULL);
}
/*
* if we are scanning forward, we need to find the first non-empty
* page (if any) in the bucket chain. since overflow pages are never
* empty, this had better be either the bucket page or the first
* overflow page.
*
* if we are scanning backward, we always go all the way to the end of
* the bucket chain.
*/
if (PageIsEmpty(page))
{
if (BlockNumberIsValid(opaque->hasho_nextblkno))
{
_hash_readnext(rel, &buf, &page, &opaque);
}
else
{
ItemPointerSetInvalid(current);
so->hashso_curbuf = InvalidBuffer;
/*
* If there is no scankeys, all tuples will satisfy the scan -
* so we continue in _hash_step to get tuples from all
* buckets. - vadim 04/29/97
*/
if (scan->numberOfKeys >= 1)
{
_hash_relbuf(rel, buf, HASH_READ);
_hash_relbuf(rel, metabuf, HASH_READ);
return ((RetrieveIndexResult) NULL);
}
}
}
}
if (ScanDirectionIsBackward(dir)) {
while (BlockNumberIsValid(opaque->hasho_nextblkno)) {
_hash_readnext(rel, &buf, &page, &opaque);
if (ScanDirectionIsBackward(dir))
{
while (BlockNumberIsValid(opaque->hasho_nextblkno))
{
_hash_readnext(rel, &buf, &page, &opaque);
}
}
}
if (!_hash_step(scan, &buf, dir, metabuf)) {
return ((RetrieveIndexResult) NULL);
}
if (!_hash_step(scan, &buf, dir, metabuf))
{
return ((RetrieveIndexResult) NULL);
}
/* if we're here, _hash_step found a valid tuple */
current = &(scan->currentItemData);
offnum = ItemPointerGetOffsetNumber(current);
page = BufferGetPage(buf);
_hash_checkpage(page, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE);
hitem = (HashItem) PageGetItem(page, PageGetItemId(page, offnum));
itup = &hitem->hash_itup;
res = FormRetrieveIndexResult(current, &(itup->t_tid));
/* if we're here, _hash_step found a valid tuple */
current = &(scan->currentItemData);
offnum = ItemPointerGetOffsetNumber(current);
page = BufferGetPage(buf);
_hash_checkpage(page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
hitem = (HashItem) PageGetItem(page, PageGetItemId(page, offnum));
itup = &hitem->hash_itup;
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
* false. Else, return true and set the CurrentItemData for the
* scan to the right thing.
*
* 'bufP' points to the buffer which contains the current page
* that we'll step through.
* If no valid record exists in the requested direction, return
* false. Else, return true and set the CurrentItemData for the
* scan to the right thing.
*
* 'metabuf' is released when this returns.
* 'bufP' points to the buffer which contains the current page
* that we'll step through.
*
* 'metabuf' is released when this returns.
*/
bool
_hash_step(IndexScanDesc scan, Buffer *bufP, ScanDirection dir, Buffer metabuf)
_hash_step(IndexScanDesc scan, Buffer * bufP, ScanDirection dir, Buffer metabuf)
{
Relation rel;
ItemPointer current;
HashScanOpaque so;
int allbuckets;
HashMetaPage metap;
Buffer buf;
Page page;
HashPageOpaque opaque;
OffsetNumber maxoff;
OffsetNumber offnum;
Bucket bucket;
BlockNumber blkno;
HashItem hitem;
IndexTuple itup;
Relation rel;
ItemPointer current;
HashScanOpaque so;
int allbuckets;
HashMetaPage metap;
Buffer buf;
Page page;
HashPageOpaque opaque;
OffsetNumber maxoff;
OffsetNumber offnum;
Bucket bucket;
BlockNumber blkno;
HashItem hitem;
IndexTuple itup;
rel = scan->relation;
current = &(scan->currentItemData);
so = (HashScanOpaque) scan->opaque;
allbuckets = (scan->numberOfKeys < 1);
rel = scan->relation;
current = &(scan->currentItemData);
so = (HashScanOpaque) scan->opaque;
allbuckets = (scan->numberOfKeys < 1);
metap = (HashMetaPage) BufferGetPage(metabuf);
_hash_checkpage((Page) metap, LH_META_PAGE);
metap = (HashMetaPage) BufferGetPage(metabuf);
_hash_checkpage((Page) metap, LH_META_PAGE);
buf = *bufP;
page = BufferGetPage(buf);
_hash_checkpage(page, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE);
opaque = (HashPageOpaque) PageGetSpecialPointer(page);
buf = *bufP;
page = BufferGetPage(buf);
_hash_checkpage(page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
opaque = (HashPageOpaque) PageGetSpecialPointer(page);
/*
* If _hash_step is called from _hash_first, current will not be
* valid, so we can't dereference it. However, in that case, we
* presumably want to start at the beginning/end of the page...
*/
maxoff = PageGetMaxOffsetNumber(page);
if (ItemPointerIsValid(current)) {
offnum = ItemPointerGetOffsetNumber(current);
} 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;
/*
* If _hash_step is called from _hash_first, current will not be
* valid, so we can't dereference it. However, in that case, we
* presumably want to start at the beginning/end of the page...
*/
maxoff = PageGetMaxOffsetNumber(page);
if (ItemPointerIsValid(current))
{
offnum = ItemPointerGetOffsetNumber(current);
}
else
{
offnum = InvalidOffsetNumber;
}
/* 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);
/*
* '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)
{
_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,80 +1,83 @@
/*-------------------------------------------------------------------------
*
* 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
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#include <postgres.h>
#include <access/hash.h>
#include <access/istrat.h>
/*
* only one valid strategy for hash tables: equality.
/*
* only one valid strategy for hash tables: equality.
*/
#ifdef NOT_USED
static StrategyNumber HTNegate[1] = {
InvalidStrategy
static StrategyNumber HTNegate[1] = {
InvalidStrategy
};
static StrategyNumber HTCommute[1] = {
HTEqualStrategyNumber
static StrategyNumber HTCommute[1] = {
HTEqualStrategyNumber
};
static StrategyNumber HTNegateCommute[1] = {
InvalidStrategy
static StrategyNumber HTNegateCommute[1] = {
InvalidStrategy
};
static StrategyEvaluationData HTEvaluationData = {
/* XXX static for simplicity */
static StrategyEvaluationData HTEvaluationData = {
/* XXX static for simplicity */
HTMaxStrategyNumber,
(StrategyTransformMap)HTNegate,
(StrategyTransformMap)HTCommute,
(StrategyTransformMap)HTNegateCommute,
{NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}
HTMaxStrategyNumber,
(StrategyTransformMap) HTNegate,
(StrategyTransformMap) HTCommute,
(StrategyTransformMap) HTNegateCommute,
{NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
};
#endif
/* ----------------------------------------------------------------
* RelationGetHashStrategy
* RelationGetHashStrategy
* ----------------------------------------------------------------
*/
#ifdef NOT_USED
static StrategyNumber
static StrategyNumber
_hash_getstrat(Relation rel,
AttrNumber attno,
RegProcedure proc)
AttrNumber attno,
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
#ifdef NOT_USED
static bool
static bool
_hash_invokestrat(Relation rel,
AttrNumber attno,
StrategyNumber strat,
Datum left,
Datum right)
AttrNumber attno,
StrategyNumber strat,
Datum left,
Datum right)
{
return (RelationInvokeStrategy(rel, &HTEvaluationData, attno, strat,
left, right));
return (RelationInvokeStrategy(rel, &HTEvaluationData, attno, strat,
left, right));
}
#endif

View File

@ -1,109 +1,110 @@
/*-------------------------------------------------------------------------
*
* btutils.c--
* Utility code for Postgres btree implementation.
* Utility code for Postgres btree implementation.
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#include <postgres.h>
#include <access/hash.h>
#include <fmgr.h>
#include <utils/memutils.h>
#include <access/iqual.h>
#ifndef HAVE_MEMMOVE
# include <regex/utils.h>
#include <regex/utils.h>
#else
# include <string.h>
#include <string.h>
#endif
ScanKey
_hash_mkscankey(Relation rel, IndexTuple itup, HashMetaPage metap)
{
ScanKey skey;
TupleDesc itupdesc;
int natts;
AttrNumber i;
Datum arg;
RegProcedure proc;
bool null;
natts = rel->rd_rel->relnatts;
itupdesc = RelationGetTupleDescriptor(rel);
skey = (ScanKey) palloc(natts * sizeof(ScanKeyData));
for (i = 0; i < natts; i++) {
arg = index_getattr(itup, i + 1, itupdesc, &null);
proc = metap->hashm_procid;
ScanKeyEntryInitialize(&skey[i],
0x0, (AttrNumber) (i + 1), proc, arg);
}
return (skey);
}
ScanKey skey;
TupleDesc itupdesc;
int natts;
AttrNumber i;
Datum arg;
RegProcedure proc;
bool null;
natts = rel->rd_rel->relnatts;
itupdesc = RelationGetTupleDescriptor(rel);
skey = (ScanKey) palloc(natts * sizeof(ScanKeyData));
for (i = 0; i < natts; i++)
{
arg = index_getattr(itup, i + 1, itupdesc, &null);
proc = metap->hashm_procid;
ScanKeyEntryInitialize(&skey[i],
0x0, (AttrNumber) (i + 1), proc, arg);
}
return (skey);
}
void
_hash_freeskey(ScanKey skey)
{
pfree(skey);
pfree(skey);
}
bool
_hash_checkqual(IndexScanDesc scan, IndexTuple itup)
{
if (scan->numberOfKeys > 0)
return (index_keytest(itup,
RelationGetTupleDescriptor(scan->relation),
scan->numberOfKeys, scan->keyData));
else
return (true);
if (scan->numberOfKeys > 0)
return (index_keytest(itup,
RelationGetTupleDescriptor(scan->relation),
scan->numberOfKeys, scan->keyData));
else
return (true);
}
HashItem
_hash_formitem(IndexTuple itup)
{
int nbytes_hitem;
HashItem hitem;
Size tuplen;
/* disallow nulls in hash keys */
if (itup->t_info & INDEX_NULL_MASK)
elog(WARN, "hash indices cannot include null keys");
/* make a copy of the index tuple with room for the sequence number */
tuplen = IndexTupleSize(itup);
nbytes_hitem = tuplen +
(sizeof(HashItemData) - sizeof(IndexTupleData));
hitem = (HashItem) palloc(nbytes_hitem);
memmove((char *) &(hitem->hash_itup), (char *) itup, tuplen);
return (hitem);
int nbytes_hitem;
HashItem hitem;
Size tuplen;
/* disallow nulls in hash keys */
if (itup->t_info & INDEX_NULL_MASK)
elog(WARN, "hash indices cannot include null keys");
/* make a copy of the index tuple with room for the sequence number */
tuplen = IndexTupleSize(itup);
nbytes_hitem = tuplen +
(sizeof(HashItemData) - sizeof(IndexTupleData));
hitem = (HashItem) palloc(nbytes_hitem);
memmove((char *) &(hitem->hash_itup), (char *) itup, tuplen);
return (hitem);
}
Bucket
_hash_call(Relation rel, HashMetaPage metap, Datum key)
{
uint32 n;
Bucket bucket;
RegProcedure proc;
proc = metap->hashm_procid;
n = (uint32) fmgr(proc, key);
bucket = n & metap->hashm_highmask;
if (bucket > metap->hashm_maxbucket)
bucket = bucket & metap->hashm_lowmask;
return (bucket);
uint32 n;
Bucket bucket;
RegProcedure proc;
proc = metap->hashm_procid;
n = (uint32) fmgr(proc, key);
bucket = n & metap->hashm_highmask;
if (bucket > metap->hashm_maxbucket)
bucket = bucket & metap->hashm_lowmask;
return (bucket);
}
/*
@ -112,12 +113,13 @@ _hash_call(Relation rel, HashMetaPage metap, Datum key)
uint32
_hash_log2(uint32 num)
{
uint32 i, limit;
limit = 1;
for (i = 0; limit < num; limit = limit << 1, i++)
;
return (i);
uint32 i,
limit;
limit = 1;
for (i = 0; limit < num; limit = limit << 1, i++)
;
return (i);
}
/*
@ -126,19 +128,20 @@ _hash_log2(uint32 num)
void
_hash_checkpage(Page page, int flags)
{
HashPageOpaque opaque;
HashPageOpaque opaque;
Assert(page);
Assert(((PageHeader)(page))->pd_lower >= (sizeof(PageHeaderData) - sizeof(ItemIdData)));
Assert(page);
Assert(((PageHeader) (page))->pd_lower >= (sizeof(PageHeaderData) - sizeof(ItemIdData)));
#if 1
Assert(((PageHeader)(page))->pd_upper <=
(BLCKSZ - DOUBLEALIGN(sizeof(HashPageOpaqueData))));
Assert(((PageHeader)(page))->pd_special ==
(BLCKSZ - DOUBLEALIGN(sizeof(HashPageOpaqueData))));
Assert(((PageHeader)(page))->pd_opaque.od_pagesize == BLCKSZ);
Assert(((PageHeader) (page))->pd_upper <=
(BLCKSZ - DOUBLEALIGN(sizeof(HashPageOpaqueData))));
Assert(((PageHeader) (page))->pd_special ==
(BLCKSZ - DOUBLEALIGN(sizeof(HashPageOpaqueData))));
Assert(((PageHeader) (page))->pd_opaque.od_pagesize == BLCKSZ);
#endif
if (flags) {
opaque = (HashPageOpaque) PageGetSpecialPointer(page);
Assert(opaque->hasho_flag & flags);
}
if (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--
* POSTGRES heap access method input/output code.
* POSTGRES heap access method input/output code.
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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
* Currently on errors, calls elog. Perhaps should return -1?
* 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.
* Currently on errors, calls elog. Perhaps should return -1?
* 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.
*
* This should be coordinated with the B-tree code.
* Probably needs to have an amdelunique to allow for
* internal index records to be deleted and reordered as needed.
* For the heap AM, this should never be needed.
* This should be coordinated with the B-tree code.
* Probably needs to have an amdelunique to allow for
* internal index records to be deleted and reordered as needed.
* For the heap AM, this should never be needed.
*/
void
RelationPutHeapTuple(Relation relation,
BlockNumber blockIndex,
HeapTuple tuple)
BlockNumber blockIndex,
HeapTuple tuple)
{
Buffer buffer;
Page pageHeader;
BlockNumber numberOfBlocks;
OffsetNumber offnum;
unsigned int len;
ItemId itemId;
Item item;
/* ----------------
* increment access statistics
* ----------------
*/
IncrHeapAccessStat(local_RelationPutHeapTuple);
IncrHeapAccessStat(global_RelationPutHeapTuple);
Assert(RelationIsValid(relation));
Assert(HeapTupleIsValid(tuple));
numberOfBlocks = RelationGetNumberOfBlocks(relation);
Assert(blockIndex < numberOfBlocks);
buffer = ReadBuffer(relation, blockIndex);
Buffer buffer;
Page pageHeader;
BlockNumber numberOfBlocks;
OffsetNumber offnum;
unsigned int len;
ItemId itemId;
Item item;
/* ----------------
* increment access statistics
* ----------------
*/
IncrHeapAccessStat(local_RelationPutHeapTuple);
IncrHeapAccessStat(global_RelationPutHeapTuple);
Assert(RelationIsValid(relation));
Assert(HeapTupleIsValid(tuple));
numberOfBlocks = RelationGetNumberOfBlocks(relation);
Assert(blockIndex < numberOfBlocks);
buffer = ReadBuffer(relation, blockIndex);
#ifndef NO_BUFFERISVALID
if (!BufferIsValid(buffer)) {
elog(WARN, "RelationPutHeapTuple: no buffer for %ld in %s",
blockIndex, &relation->rd_rel->relname);
}
if (!BufferIsValid(buffer))
{
elog(WARN, "RelationPutHeapTuple: no buffer for %ld in %s",
blockIndex, &relation->rd_rel->relname);
}
#endif
pageHeader = (Page)BufferGetPage(buffer);
len = (unsigned)DOUBLEALIGN(tuple->t_len); /* be conservative */
Assert((int)len <= PageGetFreeSpace(pageHeader));
offnum = PageAddItem((Page)pageHeader, (Item)tuple,
tuple->t_len, InvalidOffsetNumber, LP_USED);
itemId = PageGetItemId((Page)pageHeader, offnum);
item = PageGetItem((Page)pageHeader, itemId);
ItemPointerSet(&((HeapTuple)item)->t_ctid, blockIndex, offnum);
WriteBuffer(buffer);
/* return an accurate tuple */
ItemPointerSet(&tuple->t_ctid, blockIndex, offnum);
pageHeader = (Page) BufferGetPage(buffer);
len = (unsigned) DOUBLEALIGN(tuple->t_len); /* be conservative */
Assert((int) len <= PageGetFreeSpace(pageHeader));
offnum = PageAddItem((Page) pageHeader, (Item) tuple,
tuple->t_len, InvalidOffsetNumber, LP_USED);
itemId = PageGetItemId((Page) pageHeader, offnum);
item = PageGetItem((Page) pageHeader, itemId);
ItemPointerSet(&((HeapTuple) item)->t_ctid, blockIndex, offnum);
WriteBuffer(buffer);
/* return an accurate tuple */
ItemPointerSet(&tuple->t_ctid, blockIndex, offnum);
}
/*
@ -91,7 +92,7 @@ RelationPutHeapTuple(Relation relation,
* Eventually, we should cache the number of blocks in a relation somewhere.
* Until that time, this code will have to do an lseek to determine the number
* of blocks in a relation.
*
*
* This code should ideally do at most 4 semops, 1 lseek, and possibly 1 write
* to do an append; it's possible to eliminate 2 of the semops if we do direct
* buffer stuff (!); the lseek and the write can go if we get
@ -107,70 +108,70 @@ RelationPutHeapTuple(Relation relation,
void
RelationPutHeapTupleAtEnd(Relation relation, HeapTuple tuple)
{
Buffer buffer;
Page pageHeader;
BlockNumber lastblock;
OffsetNumber offnum;
unsigned int len;
ItemId itemId;
Item item;
Assert(RelationIsValid(relation));
Assert(HeapTupleIsValid(tuple));
/*
* XXX This does an lseek - VERY expensive - but at the moment it
* is the only way to accurately determine how many blocks are in
* a relation. A good optimization would be to get this to actually
* work properly.
*/
lastblock = RelationGetNumberOfBlocks(relation);
if (lastblock == 0)
Buffer buffer;
Page pageHeader;
BlockNumber lastblock;
OffsetNumber offnum;
unsigned int len;
ItemId itemId;
Item item;
Assert(RelationIsValid(relation));
Assert(HeapTupleIsValid(tuple));
/*
* XXX This does an lseek - VERY expensive - but at the moment it is
* the only way to accurately determine how many blocks are in a
* relation. A good optimization would be to get this to actually
* work properly.
*/
lastblock = RelationGetNumberOfBlocks(relation);
if (lastblock == 0)
{
buffer = ReadBuffer(relation, lastblock);
pageHeader = (Page)BufferGetPage(buffer);
if (PageIsNew((PageHeader) pageHeader))
buffer = ReadBuffer(relation, lastblock);
pageHeader = (Page) BufferGetPage(buffer);
if (PageIsNew((PageHeader) pageHeader))
{
buffer = ReleaseAndReadBuffer(buffer, relation, P_NEW);
pageHeader = (Page)BufferGetPage(buffer);
PageInit(pageHeader, BufferGetPageSize(buffer), 0);
buffer = ReleaseAndReadBuffer(buffer, relation, P_NEW);
pageHeader = (Page) BufferGetPage(buffer);
PageInit(pageHeader, BufferGetPageSize(buffer), 0);
}
}
else
buffer = ReadBuffer(relation, lastblock - 1);
pageHeader = (Page)BufferGetPage(buffer);
len = (unsigned)DOUBLEALIGN(tuple->t_len); /* be conservative */
/*
* Note that this is true if the above returned a bogus page, which
* it will do for a completely empty relation.
*/
if (len > PageGetFreeSpace(pageHeader))
else
buffer = ReadBuffer(relation, lastblock - 1);
pageHeader = (Page) BufferGetPage(buffer);
len = (unsigned) DOUBLEALIGN(tuple->t_len); /* be conservative */
/*
* Note that this is true if the above returned a bogus page, which it
* will do for a completely empty relation.
*/
if (len > PageGetFreeSpace(pageHeader))
{
buffer = ReleaseAndReadBuffer(buffer, relation, P_NEW);
pageHeader = (Page)BufferGetPage(buffer);
PageInit(pageHeader, BufferGetPageSize(buffer), 0);
if (len > PageGetFreeSpace(pageHeader))
elog(WARN, "Tuple is too big: size %d", len);
buffer = ReleaseAndReadBuffer(buffer, relation, P_NEW);
pageHeader = (Page) BufferGetPage(buffer);
PageInit(pageHeader, BufferGetPageSize(buffer), 0);
if (len > PageGetFreeSpace(pageHeader))
elog(WARN, "Tuple is too big: size %d", len);
}
offnum = PageAddItem((Page)pageHeader, (Item)tuple,
tuple->t_len, InvalidOffsetNumber, LP_USED);
itemId = PageGetItemId((Page)pageHeader, offnum);
item = PageGetItem((Page)pageHeader, itemId);
lastblock = BufferGetBlockNumber(buffer);
ItemPointerSet(&((HeapTuple)item)->t_ctid, lastblock, offnum);
/* return an accurate tuple */
ItemPointerSet(&tuple->t_ctid, lastblock, offnum);
WriteBuffer(buffer);
offnum = PageAddItem((Page) pageHeader, (Item) tuple,
tuple->t_len, InvalidOffsetNumber, LP_USED);
itemId = PageGetItemId((Page) pageHeader, offnum);
item = PageGetItem((Page) pageHeader, itemId);
lastblock = BufferGetBlockNumber(buffer);
ItemPointerSet(&((HeapTuple) item)->t_ctid, lastblock, offnum);
/* return an accurate tuple */
ItemPointerSet(&tuple->t_ctid, lastblock, offnum);
WriteBuffer(buffer);
}

View File

@ -1,16 +1,16 @@
/*-------------------------------------------------------------------------
*
* 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
*
*
* 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
* initam should be moved someplace else.
* initam should be moved someplace else.
*
*-------------------------------------------------------------------------
*/
@ -23,322 +23,327 @@
#include <utils/mcxt.h>
#ifndef HAVE_MEMMOVE
# include <regex/utils.h>
#include <regex/utils.h>
#else
# include <string.h>
#include <string.h>
#endif
static void InitHeapAccessStatistics(void);
static void InitHeapAccessStatistics(void);
/* ----------------
* InitHeapAccessStatistics
* InitHeapAccessStatistics
* ----------------
*/
HeapAccessStatistics heap_access_stats = (HeapAccessStatistics) NULL;
static void
InitHeapAccessStatistics()
InitHeapAccessStatistics()
{
MemoryContext oldContext;
HeapAccessStatistics stats;
/* ----------------
* make sure we don't initialize things twice
* ----------------
*/
if (heap_access_stats != NULL)
return;
/* ----------------
* allocate statistics structure from the top memory context
* ----------------
*/
oldContext = MemoryContextSwitchTo(TopMemoryContext);
stats = (HeapAccessStatistics)
palloc(sizeof(HeapAccessStatisticsData));
/* ----------------
* initialize fields to default values
* ----------------
*/
stats->global_open = 0;
stats->global_openr = 0;
stats->global_close = 0;
stats->global_beginscan = 0;
stats->global_rescan = 0;
stats->global_endscan = 0;
stats->global_getnext = 0;
stats->global_fetch = 0;
stats->global_insert = 0;
stats->global_delete = 0;
stats->global_replace = 0;
stats->global_markpos = 0;
stats->global_restrpos = 0;
stats->global_BufferGetRelation = 0;
stats->global_RelationIdGetRelation = 0;
stats->global_RelationIdGetRelation_Buf = 0;
stats->global_getreldesc = 0;
stats->global_heapgettup = 0;
stats->global_RelationPutHeapTuple = 0;
stats->global_RelationPutLongHeapTuple = 0;
stats->local_open = 0;
stats->local_openr = 0;
stats->local_close = 0;
stats->local_beginscan = 0;
stats->local_rescan = 0;
stats->local_endscan = 0;
stats->local_getnext = 0;
stats->local_fetch = 0;
stats->local_insert = 0;
stats->local_delete = 0;
stats->local_replace = 0;
stats->local_markpos = 0;
stats->local_restrpos = 0;
stats->local_BufferGetRelation = 0;
stats->local_RelationIdGetRelation = 0;
stats->local_RelationIdGetRelation_Buf = 0;
stats->local_getreldesc = 0;
stats->local_heapgettup = 0;
stats->local_RelationPutHeapTuple = 0;
stats->local_RelationPutLongHeapTuple = 0;
stats->local_RelationNameGetRelation = 0;
stats->global_RelationNameGetRelation = 0;
/* ----------------
* record init times
* ----------------
*/
time(&stats->init_global_timestamp);
time(&stats->local_reset_timestamp);
time(&stats->last_request_timestamp);
/* ----------------
* return to old memory context
* ----------------
*/
MemoryContextSwitchTo(oldContext);
heap_access_stats = stats;
MemoryContext oldContext;
HeapAccessStatistics stats;
/* ----------------
* make sure we don't initialize things twice
* ----------------
*/
if (heap_access_stats != NULL)
return;
/* ----------------
* allocate statistics structure from the top memory context
* ----------------
*/
oldContext = MemoryContextSwitchTo(TopMemoryContext);
stats = (HeapAccessStatistics)
palloc(sizeof(HeapAccessStatisticsData));
/* ----------------
* initialize fields to default values
* ----------------
*/
stats->global_open = 0;
stats->global_openr = 0;
stats->global_close = 0;
stats->global_beginscan = 0;
stats->global_rescan = 0;
stats->global_endscan = 0;
stats->global_getnext = 0;
stats->global_fetch = 0;
stats->global_insert = 0;
stats->global_delete = 0;
stats->global_replace = 0;
stats->global_markpos = 0;
stats->global_restrpos = 0;
stats->global_BufferGetRelation = 0;
stats->global_RelationIdGetRelation = 0;
stats->global_RelationIdGetRelation_Buf = 0;
stats->global_getreldesc = 0;
stats->global_heapgettup = 0;
stats->global_RelationPutHeapTuple = 0;
stats->global_RelationPutLongHeapTuple = 0;
stats->local_open = 0;
stats->local_openr = 0;
stats->local_close = 0;
stats->local_beginscan = 0;
stats->local_rescan = 0;
stats->local_endscan = 0;
stats->local_getnext = 0;
stats->local_fetch = 0;
stats->local_insert = 0;
stats->local_delete = 0;
stats->local_replace = 0;
stats->local_markpos = 0;
stats->local_restrpos = 0;
stats->local_BufferGetRelation = 0;
stats->local_RelationIdGetRelation = 0;
stats->local_RelationIdGetRelation_Buf = 0;
stats->local_getreldesc = 0;
stats->local_heapgettup = 0;
stats->local_RelationPutHeapTuple = 0;
stats->local_RelationPutLongHeapTuple = 0;
stats->local_RelationNameGetRelation = 0;
stats->global_RelationNameGetRelation = 0;
/* ----------------
* record init times
* ----------------
*/
time(&stats->init_global_timestamp);
time(&stats->local_reset_timestamp);
time(&stats->last_request_timestamp);
/* ----------------
* return to old memory context
* ----------------
*/
MemoryContextSwitchTo(oldContext);
heap_access_stats = stats;
}
#ifdef NOT_USED
/* ----------------
* ResetHeapAccessStatistics
* ResetHeapAccessStatistics
* ----------------
*/
void
ResetHeapAccessStatistics()
ResetHeapAccessStatistics()
{
HeapAccessStatistics stats;
/* ----------------
* do nothing if stats aren't initialized
* ----------------
*/
if (heap_access_stats == NULL)
return;
stats = heap_access_stats;
/* ----------------
* reset local counts
* ----------------
*/
stats->local_open = 0;
stats->local_openr = 0;
stats->local_close = 0;
stats->local_beginscan = 0;
stats->local_rescan = 0;
stats->local_endscan = 0;
stats->local_getnext = 0;
stats->local_fetch = 0;
stats->local_insert = 0;
stats->local_delete = 0;
stats->local_replace = 0;
stats->local_markpos = 0;
stats->local_restrpos = 0;
stats->local_BufferGetRelation = 0;
stats->local_RelationIdGetRelation = 0;
stats->local_RelationIdGetRelation_Buf = 0;
stats->local_getreldesc = 0;
stats->local_heapgettup = 0;
stats->local_RelationPutHeapTuple = 0;
stats->local_RelationPutLongHeapTuple = 0;
/* ----------------
* reset local timestamps
* ----------------
*/
time(&stats->local_reset_timestamp);
time(&stats->last_request_timestamp);
HeapAccessStatistics stats;
/* ----------------
* do nothing if stats aren't initialized
* ----------------
*/
if (heap_access_stats == NULL)
return;
stats = heap_access_stats;
/* ----------------
* reset local counts
* ----------------
*/
stats->local_open = 0;
stats->local_openr = 0;
stats->local_close = 0;
stats->local_beginscan = 0;
stats->local_rescan = 0;
stats->local_endscan = 0;
stats->local_getnext = 0;
stats->local_fetch = 0;
stats->local_insert = 0;
stats->local_delete = 0;
stats->local_replace = 0;
stats->local_markpos = 0;
stats->local_restrpos = 0;
stats->local_BufferGetRelation = 0;
stats->local_RelationIdGetRelation = 0;
stats->local_RelationIdGetRelation_Buf = 0;
stats->local_getreldesc = 0;
stats->local_heapgettup = 0;
stats->local_RelationPutHeapTuple = 0;
stats->local_RelationPutLongHeapTuple = 0;
/* ----------------
* reset local timestamps
* ----------------
*/
time(&stats->local_reset_timestamp);
time(&stats->last_request_timestamp);
}
#endif
#ifdef NOT_USED
/* ----------------
* GetHeapAccessStatistics
* GetHeapAccessStatistics
* ----------------
*/
HeapAccessStatistics GetHeapAccessStatistics()
HeapAccessStatistics
GetHeapAccessStatistics()
{
HeapAccessStatistics stats;
/* ----------------
* return nothing if stats aren't initialized
* ----------------
*/
if (heap_access_stats == NULL)
return NULL;
/* ----------------
* record the current request time
* ----------------
*/
time(&heap_access_stats->last_request_timestamp);
/* ----------------
* allocate a copy of the stats and return it to the caller.
* ----------------
*/
stats = (HeapAccessStatistics)
palloc(sizeof(HeapAccessStatisticsData));
memmove(stats,
heap_access_stats,
sizeof(HeapAccessStatisticsData));
return stats;
HeapAccessStatistics stats;
/* ----------------
* return nothing if stats aren't initialized
* ----------------
*/
if (heap_access_stats == NULL)
return NULL;
/* ----------------
* record the current request time
* ----------------
*/
time(&heap_access_stats->last_request_timestamp);
/* ----------------
* allocate a copy of the stats and return it to the caller.
* ----------------
*/
stats = (HeapAccessStatistics)
palloc(sizeof(HeapAccessStatisticsData));
memmove(stats,
heap_access_stats,
sizeof(HeapAccessStatisticsData));
return stats;
}
#endif
#ifdef NOT_USED
/* ----------------
* PrintHeapAccessStatistics
* PrintHeapAccessStatistics
* ----------------
*/
void
PrintHeapAccessStatistics(HeapAccessStatistics stats)
{
/* ----------------
* return nothing if stats aren't valid
* ----------------
*/
if (stats == NULL)
return;
printf("======== heap am statistics ========\n");
printf("init_global_timestamp: %s",
ctime(&(stats->init_global_timestamp)));
printf("local_reset_timestamp: %s",
ctime(&(stats->local_reset_timestamp)));
printf("last_request_timestamp: %s",
ctime(&(stats->last_request_timestamp)));
printf("local/global_open: %6d/%6d\n",
stats->local_open, stats->global_open);
printf("local/global_openr: %6d/%6d\n",
stats->local_openr, stats->global_openr);
printf("local/global_close: %6d/%6d\n",
stats->local_close, stats->global_close);
printf("local/global_beginscan: %6d/%6d\n",
stats->local_beginscan, stats->global_beginscan);
printf("local/global_rescan: %6d/%6d\n",
stats->local_rescan, stats->global_rescan);
printf("local/global_endscan: %6d/%6d\n",
stats->local_endscan, stats->global_endscan);
printf("local/global_getnext: %6d/%6d\n",
stats->local_getnext, stats->global_getnext);
printf("local/global_fetch: %6d/%6d\n",
stats->local_fetch, stats->global_fetch);
printf("local/global_insert: %6d/%6d\n",
stats->local_insert, stats->global_insert);
printf("local/global_delete: %6d/%6d\n",
stats->local_delete, stats->global_delete);
printf("local/global_replace: %6d/%6d\n",
stats->local_replace, stats->global_replace);
printf("local/global_markpos: %6d/%6d\n",
stats->local_markpos, stats->global_markpos);
printf("local/global_restrpos: %6d/%6d\n",
stats->local_restrpos, stats->global_restrpos);
printf("================\n");
printf("local/global_BufferGetRelation: %6d/%6d\n",
stats->local_BufferGetRelation,
stats->global_BufferGetRelation);
printf("local/global_RelationIdGetRelation: %6d/%6d\n",
stats->local_RelationIdGetRelation,
stats->global_RelationIdGetRelation);
printf("local/global_RelationIdGetRelation_Buf: %6d/%6d\n",
stats->local_RelationIdGetRelation_Buf,
stats->global_RelationIdGetRelation_Buf);
printf("local/global_getreldesc: %6d/%6d\n",
stats->local_getreldesc, stats->global_getreldesc);
printf("local/global_heapgettup: %6d/%6d\n",
stats->local_heapgettup, stats->global_heapgettup);
printf("local/global_RelationPutHeapTuple: %6d/%6d\n",
stats->local_RelationPutHeapTuple,
stats->global_RelationPutHeapTuple);
printf("local/global_RelationPutLongHeapTuple: %6d/%6d\n",
stats->local_RelationPutLongHeapTuple,
stats->global_RelationPutLongHeapTuple);
printf("===================================\n");
printf("\n");
/* ----------------
* return nothing if stats aren't valid
* ----------------
*/
if (stats == NULL)
return;
printf("======== heap am statistics ========\n");
printf("init_global_timestamp: %s",
ctime(&(stats->init_global_timestamp)));
printf("local_reset_timestamp: %s",
ctime(&(stats->local_reset_timestamp)));
printf("last_request_timestamp: %s",
ctime(&(stats->last_request_timestamp)));
printf("local/global_open: %6d/%6d\n",
stats->local_open, stats->global_open);
printf("local/global_openr: %6d/%6d\n",
stats->local_openr, stats->global_openr);
printf("local/global_close: %6d/%6d\n",
stats->local_close, stats->global_close);
printf("local/global_beginscan: %6d/%6d\n",
stats->local_beginscan, stats->global_beginscan);
printf("local/global_rescan: %6d/%6d\n",
stats->local_rescan, stats->global_rescan);
printf("local/global_endscan: %6d/%6d\n",
stats->local_endscan, stats->global_endscan);
printf("local/global_getnext: %6d/%6d\n",
stats->local_getnext, stats->global_getnext);
printf("local/global_fetch: %6d/%6d\n",
stats->local_fetch, stats->global_fetch);
printf("local/global_insert: %6d/%6d\n",
stats->local_insert, stats->global_insert);
printf("local/global_delete: %6d/%6d\n",
stats->local_delete, stats->global_delete);
printf("local/global_replace: %6d/%6d\n",
stats->local_replace, stats->global_replace);
printf("local/global_markpos: %6d/%6d\n",
stats->local_markpos, stats->global_markpos);
printf("local/global_restrpos: %6d/%6d\n",
stats->local_restrpos, stats->global_restrpos);
printf("================\n");
printf("local/global_BufferGetRelation: %6d/%6d\n",
stats->local_BufferGetRelation,
stats->global_BufferGetRelation);
printf("local/global_RelationIdGetRelation: %6d/%6d\n",
stats->local_RelationIdGetRelation,
stats->global_RelationIdGetRelation);
printf("local/global_RelationIdGetRelation_Buf: %6d/%6d\n",
stats->local_RelationIdGetRelation_Buf,
stats->global_RelationIdGetRelation_Buf);
printf("local/global_getreldesc: %6d/%6d\n",
stats->local_getreldesc, stats->global_getreldesc);
printf("local/global_heapgettup: %6d/%6d\n",
stats->local_heapgettup, stats->global_heapgettup);
printf("local/global_RelationPutHeapTuple: %6d/%6d\n",
stats->local_RelationPutHeapTuple,
stats->global_RelationPutHeapTuple);
printf("local/global_RelationPutLongHeapTuple: %6d/%6d\n",
stats->local_RelationPutLongHeapTuple,
stats->global_RelationPutLongHeapTuple);
printf("===================================\n");
printf("\n");
}
#endif
#ifdef NOT_USED
/* ----------------
* PrintAndFreeHeapAccessStatistics
* PrintAndFreeHeapAccessStatistics
* ----------------
*/
void
PrintAndFreeHeapAccessStatistics(HeapAccessStatistics stats)
{
PrintHeapAccessStatistics(stats);
if (stats != NULL)
pfree(stats);
PrintHeapAccessStatistics(stats);
if (stats != NULL)
pfree(stats);
}
#endif
/* ----------------------------------------------------------------
* access method initialization
* access method initialization
* ----------------------------------------------------------------
*/
/* ----------------
* initam should someday be moved someplace else.
* initam should someday be moved someplace else.
* ----------------
*/
void
initam(void)
{
/* ----------------
* initialize heap statistics.
* ----------------
*/
InitHeapAccessStatistics();
/* ----------------
* initialize heap statistics.
* ----------------
*/
InitHeapAccessStatistics();
}

View File

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

View File

@ -1,80 +1,80 @@
/*-------------------------------------------------------------------------
*
* indexam.c--
* general index access method routines
* general index access method routines
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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
* index_open - open an index relation by relationId
* index_openr - open a index relation by name
* index_close - close a index relation
* index_beginscan - start a scan of an index
* index_rescan - restart a scan of an index
* index_endscan - end a scan
* index_insert - insert an index tuple into a relation
* index_delete - delete an item from an index relation
* index_markpos - mark a scan position
* index_restrpos - restore a scan position
* index_getnext - get the next tuple from a scan
* ** index_fetch - retrieve tuple with tid
* index_open - open an index relation by relationId
* index_openr - open a index relation by name
* index_close - close a index relation
* index_beginscan - start a scan of an index
* index_rescan - restart a scan of an index
* index_endscan - end a scan
* index_insert - insert an index tuple into a relation
* index_delete - delete an item from an index relation
* index_markpos - mark a scan position
* index_restrpos - restore a scan position
* index_getnext - get the next tuple from a scan
* ** index_fetch - retrieve tuple with tid
* ** index_replace - replace a tuple
* ** index_getattr - get an attribute from an index tuple
* index_getprocid - get a support procedure id from the rel tuple
*
* IndexScanIsValid - check index scan
* index_getprocid - get a support procedure id from the rel tuple
*
* IndexScanIsValid - check index scan
*
* NOTES
* This file contains the index_ routines which used
* to be a scattered collection of stuff in access/genam.
* This file contains the index_ routines which used
* to be a scattered collection of stuff in access/genam.
*
* The ** routines: index_fetch, index_replace, and index_getattr
* have not yet been implemented. They may not be needed.
* The ** routines: index_fetch, index_replace, and index_getattr
* have not yet been implemented. They may not be needed.
*
* old comments
* Scans are implemented as follows:
* Scans are implemented as follows:
*
* `0' represents an invalid item pointer.
* `-' represents an unknown item pointer.
* `X' represents a known item pointers.
* `+' represents known or invalid item pointers.
* `*' represents any item pointers.
* `0' represents an invalid item pointer.
* `-' represents an unknown item pointer.
* `X' represents a known item pointers.
* `+' represents known or invalid item pointers.
* `*' represents any item pointers.
*
* State is represented by a triple of these symbols in the order of
* previous, current, next. Note that the case of reverse scans works
* identically.
* State is represented by a triple of these symbols in the order of
* previous, current, next. Note that the case of reverse scans works
* identically.
*
* State Result
* (1) + + - + 0 0 (if the next item pointer is invalid)
* (2) + X - (otherwise)
* (3) * 0 0 * 0 0 (no change)
* (4) + X 0 X 0 0 (shift)
* (5) * + X + X - (shift, add unknown)
* State Result
* (1) + + - + 0 0 (if the next item pointer is invalid)
* (2) + X - (otherwise)
* (3) * 0 0 * 0 0 (no change)
* (4) + X 0 X 0 0 (shift)
* (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
* next item pointer using the flags.
* Note: It would be possible to cache the status of the previous and
* next item pointer using the flags.
*
*-------------------------------------------------------------------------
*/
#include <postgres.h>
#include <access/genam.h>
#include <access/genam.h>
#include <utils/relcache.h>
#include <fmgr.h>
#include <storage/lmgr.h>
#include <access/heapam.h>
/* ----------------
* 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
* defined in heapam.h
* 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
* defined in heapam.h
* ----------------
*/
#undef delete
@ -88,314 +88,320 @@
#undef amgettuple
/* ----------------------------------------------------------------
* macros used in index_ routines
* macros used in index_ routines
* ----------------------------------------------------------------
*/
#define RELATION_CHECKS \
Assert(RelationIsValid(relation)); \
Assert(PointerIsValid(relation->rd_am))
Assert(PointerIsValid(relation->rd_am))
#define SCAN_CHECKS \
Assert(IndexScanIsValid(scan)); \
Assert(RelationIsValid(scan->relation)); \
Assert(PointerIsValid(scan->relation->rd_am))
Assert(IndexScanIsValid(scan)); \
Assert(RelationIsValid(scan->relation)); \
Assert(PointerIsValid(scan->relation->rd_am))
#define GET_REL_PROCEDURE(x,y) \
procedure = relation->rd_am->y; \
if (! RegProcedureIsValid(procedure)) \
elog(WARN, "index_%s: invalid %s regproc", \
CppAsString(x), CppAsString(y))
procedure = relation->rd_am->y; \
if (! RegProcedureIsValid(procedure)) \
elog(WARN, "index_%s: invalid %s regproc", \
CppAsString(x), CppAsString(y))
#define GET_SCAN_PROCEDURE(x,y) \
procedure = scan->relation->rd_am->y; \
if (! RegProcedureIsValid(procedure)) \
elog(WARN, "index_%s: invalid %s regproc", \
CppAsString(x), CppAsString(y))
procedure = scan->relation->rd_am->y; \
if (! RegProcedureIsValid(procedure)) \
elog(WARN, "index_%s: invalid %s regproc", \
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
* to open/close index relations.
* presently the relcache routines do all the work we need
* to open/close index relations.
* ----------------
*/
Relation
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
* to open/close index relations.
* presently the relcache routines do all the work we need
* to open/close index relations.
* ----------------
*/
Relation
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
* to open/close index relations.
* presently the relcache routines do all the work we need
* to open/close index relations.
* ----------------
*/
void
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
index_insert(Relation relation,
Datum *datum,
char *nulls,
ItemPointer heap_t_ctid,
Relation heapRel)
Datum * datum,
char *nulls,
ItemPointer heap_t_ctid,
Relation heapRel)
{
RegProcedure procedure;
InsertIndexResult specificResult;
RELATION_CHECKS;
GET_REL_PROCEDURE(insert,aminsert);
/* ----------------
* have the am's insert proc do all the work.
* ----------------
*/
specificResult = (InsertIndexResult)
fmgr(procedure, relation, datum, nulls, heap_t_ctid, heapRel, NULL);
/* ----------------
* the insert proc is supposed to return a "specific result" and
* this routine has to return a "general result" so after we get
* something back from the insert proc, we allocate a
* "general result" and copy some crap between the two.
*
* As far as I'm concerned all this result shit is needlessly c
* omplicated and should be eliminated. -cim 1/19/91
*
* mao concurs. regardless of how we feel here, however, it is
* important to free memory we don't intend to return to anyone.
* 2/28/91
*
* this "general result" crap is now gone. -ay 3/6/95
* ----------------
*/
return (specificResult);
RegProcedure procedure;
InsertIndexResult specificResult;
RELATION_CHECKS;
GET_REL_PROCEDURE(insert, aminsert);
/* ----------------
* have the am's insert proc do all the work.
* ----------------
*/
specificResult = (InsertIndexResult)
fmgr(procedure, relation, datum, nulls, heap_t_ctid, heapRel, NULL);
/* ----------------
* the insert proc is supposed to return a "specific result" and
* this routine has to return a "general result" so after we get
* something back from the insert proc, we allocate a
* "general result" and copy some crap between the two.
*
* As far as I'm concerned all this result shit is needlessly c
* omplicated and should be eliminated. -cim 1/19/91
*
* mao concurs. regardless of how we feel here, however, it is
* important to free memory we don't intend to return to anyone.
* 2/28/91
*
* this "general result" crap is now gone. -ay 3/6/95
* ----------------
*/
return (specificResult);
}
/* ----------------
* index_delete - delete an item from an index relation
* index_delete - delete an item from an index relation
* ----------------
*/
void
index_delete(Relation relation, ItemPointer indexItem)
{
RegProcedure procedure;
RELATION_CHECKS;
GET_REL_PROCEDURE(delete,amdelete);
fmgr(procedure, relation, indexItem);
RegProcedure procedure;
RELATION_CHECKS;
GET_REL_PROCEDURE(delete, amdelete);
fmgr(procedure, relation, indexItem);
}
/* ----------------
* index_beginscan - start a scan of an index
* index_beginscan - start a scan of an index
* ----------------
*/
IndexScanDesc
index_beginscan(Relation relation,
bool scanFromEnd,
uint16 numberOfKeys,
ScanKey key)
bool scanFromEnd,
uint16 numberOfKeys,
ScanKey key)
{
IndexScanDesc scandesc;
RegProcedure procedure;
RELATION_CHECKS;
GET_REL_PROCEDURE(beginscan,ambeginscan);
RelationSetRIntentLock(relation);
scandesc = (IndexScanDesc)
fmgr(procedure, relation, scanFromEnd, numberOfKeys, key);
return scandesc;
IndexScanDesc scandesc;
RegProcedure procedure;
RELATION_CHECKS;
GET_REL_PROCEDURE(beginscan, ambeginscan);
RelationSetRIntentLock(relation);
scandesc = (IndexScanDesc)
fmgr(procedure, relation, scanFromEnd, numberOfKeys, key);
return scandesc;
}
/* ----------------
* index_rescan - restart a scan of an index
* index_rescan - restart a scan of an index
* ----------------
*/
void
index_rescan(IndexScanDesc scan, bool scanFromEnd, ScanKey key)
{
RegProcedure procedure;
SCAN_CHECKS;
GET_SCAN_PROCEDURE(rescan,amrescan);
fmgr(procedure, scan, scanFromEnd, key);
RegProcedure procedure;
SCAN_CHECKS;
GET_SCAN_PROCEDURE(rescan, amrescan);
fmgr(procedure, scan, scanFromEnd, key);
}
/* ----------------
* index_endscan - end a scan
* index_endscan - end a scan
* ----------------
*/
void
index_endscan(IndexScanDesc scan)
{
RegProcedure procedure;
SCAN_CHECKS;
GET_SCAN_PROCEDURE(endscan,amendscan);
fmgr(procedure, scan);
RelationUnsetRIntentLock(scan->relation);
RegProcedure procedure;
SCAN_CHECKS;
GET_SCAN_PROCEDURE(endscan, amendscan);
fmgr(procedure, scan);
RelationUnsetRIntentLock(scan->relation);
}
#ifdef NOT_USED
/* ----------------
* index_markpos - mark a scan position
* index_markpos - mark a scan position
* ----------------
*/
void
index_markpos(IndexScanDesc scan)
{
RegProcedure procedure;
SCAN_CHECKS;
GET_SCAN_PROCEDURE(markpos,ammarkpos);
fmgr(procedure, scan);
RegProcedure procedure;
SCAN_CHECKS;
GET_SCAN_PROCEDURE(markpos, ammarkpos);
fmgr(procedure, scan);
}
#endif
#ifdef NOT_USED
/* ----------------
* index_restrpos - restore a scan position
* index_restrpos - restore a scan position
* ----------------
*/
void
index_restrpos(IndexScanDesc scan)
{
RegProcedure procedure;
SCAN_CHECKS;
GET_SCAN_PROCEDURE(restrpos,amrestrpos);
fmgr(procedure, scan);
RegProcedure procedure;
SCAN_CHECKS;
GET_SCAN_PROCEDURE(restrpos, amrestrpos);
fmgr(procedure, scan);
}
#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
index_getnext(IndexScanDesc scan,
ScanDirection direction)
ScanDirection direction)
{
RegProcedure procedure;
RetrieveIndexResult result;
SCAN_CHECKS;
GET_SCAN_PROCEDURE(getnext,amgettuple);
/* ----------------
* have the am's gettuple proc do all the work.
* ----------------
*/
result = (RetrieveIndexResult)
fmgr(procedure, scan, direction);
return result;
RegProcedure procedure;
RetrieveIndexResult result;
SCAN_CHECKS;
GET_SCAN_PROCEDURE(getnext, amgettuple);
/* ----------------
* have the am's gettuple proc do all the work.
* ----------------
*/
result = (RetrieveIndexResult)
fmgr(procedure, scan, direction);
return result;
}
/* ----------------
* index_getprocid
* index_getprocid
*
* Some indexed access methods may require support routines that are
* not in the operator class/operator model imposed by pg_am. These
* access methods may store the OIDs of registered procedures they
* 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
* access method. The general index code doesn't know anything about
* the routines involved; it just builds an ordered list of them for
* each attribute on which an index is defined.
* Some indexed access methods may require support routines that are
* not in the operator class/operator model imposed by pg_am. These
* access methods may store the OIDs of registered procedures they
* 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
* access method. The general index code doesn't know anything about
* the routines involved; it just builds an ordered list of them for
* each attribute on which an index is defined.
*
* This routine returns the requested procedure OID for a particular
* indexed attribute.
* This routine returns the requested procedure OID for a particular
* indexed attribute.
* ----------------
*/
RegProcedure
index_getprocid(Relation irel,
AttrNumber attnum,
uint16 procnum)
AttrNumber attnum,
uint16 procnum)
{
RegProcedure *loc;
int natts;
natts = irel->rd_rel->relnatts;
loc = irel->rd_support;
RegProcedure *loc;
int natts;
Assert(loc != NULL);
return (loc[(natts * (procnum - 1)) + (attnum - 1)]);
natts = irel->rd_rel->relnatts;
loc = irel->rd_support;
Assert(loc != NULL);
return (loc[(natts * (procnum - 1)) + (attnum - 1)]);
}
Datum
GetIndexValue(HeapTuple tuple,
TupleDesc hTupDesc,
int attOff,
AttrNumber attrNums[],
FuncIndexInfo *fInfo,
bool *attNull,
Buffer buffer)
TupleDesc hTupDesc,
int attOff,
AttrNumber attrNums[],
FuncIndexInfo * fInfo,
bool * attNull,
Buffer buffer)
{
Datum returnVal;
bool isNull;
if (PointerIsValid(fInfo) && FIgetProcOid(fInfo) != InvalidOid) {
int i;
Datum *attData = (Datum *)palloc(FIgetnArgs(fInfo)*sizeof(Datum));
for (i = 0; i < FIgetnArgs(fInfo); i++) {
attData[i] = (Datum) heap_getattr(tuple,
buffer,
attrNums[i],
hTupDesc,
attNull);
Datum returnVal;
bool isNull;
if (PointerIsValid(fInfo) && FIgetProcOid(fInfo) != InvalidOid)
{
int i;
Datum *attData = (Datum *) palloc(FIgetnArgs(fInfo) * sizeof(Datum));
for (i = 0; i < FIgetnArgs(fInfo); i++)
{
attData[i] = (Datum) heap_getattr(tuple,
buffer,
attrNums[i],
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),
FIgetnArgs(fInfo),
(char **) attData,
&isNull);
pfree(attData);
*attNull = FALSE;
}else {
returnVal = (Datum) heap_getattr(tuple, buffer, attrNums[attOff],
hTupDesc, attNull);
}
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--
* Comparison functions for btree access method.
* Comparison functions for btree access method.
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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
* These functions are stored in pg_amproc. For each operator class
* defined on btrees, they compute
* NOTES
* These functions are stored in pg_amproc. For each operator class
* defined on btrees, they compute
*
* compare(a, b):
* < 0 if a < b,
* = 0 if a == b,
* > 0 if a > b.
* compare(a, b):
* < 0 if a < b,
* = 0 if a == b,
* > 0 if a > b.
*-------------------------------------------------------------------------
*/
@ -30,168 +30,171 @@
int32
btint2cmp(int16 a, int16 b)
{
return ((int32) (a - b));
return ((int32) (a - b));
}
int32
btint4cmp(int32 a, int32 b)
{
return (a - b);
return (a - b);
}
int32
btint24cmp(int16 a, int32 b)
{
return (((int32) a) - b);
return (((int32) a) - b);
}
int32
btint42cmp(int32 a, int16 b)
{
return (a - ((int32) b));
return (a - ((int32) b));
}
int32
btfloat4cmp(float32 a, float32 b)
{
if (*a > *b)
return (1);
else if (*a == *b)
return (0);
else
return (-1);
if (*a > *b)
return (1);
else if (*a == *b)
return (0);
else
return (-1);
}
int32
btfloat8cmp(float64 a, float64 b)
{
if (*a > *b)
return (1);
else if (*a == *b)
return (0);
else
return (-1);
if (*a > *b)
return (1);
else if (*a == *b)
return (0);
else
return (-1);
}
int32
btoidcmp(Oid a, Oid b)
{
if (a > b)
return (1);
else if (a == b)
return (0);
else
return (-1);
if (a > b)
return (1);
else if (a == b)
return (0);
else
return (-1);
}
int32
btabstimecmp(AbsoluteTime a, AbsoluteTime b)
{
if (AbsoluteTimeIsBefore(a, b))
return (-1);
else if (AbsoluteTimeIsBefore(b, a))
return (1);
else
return (0);
if (AbsoluteTimeIsBefore(a, b))
return (-1);
else if (AbsoluteTimeIsBefore(b, a))
return (1);
else
return (0);
}
int32
btcharcmp(char a, char b)
{
return ((int32) ((uint8)a - (uint8)b));
return ((int32) ((uint8) a - (uint8) b));
}
int32
btchar2cmp(uint16 a, uint16 b)
{
return (strncmp((char *) &a, (char *) &b, 2));
return (strncmp((char *) &a, (char *) &b, 2));
}
int32
btchar4cmp(uint32 a, uint32 b)
{
return (strncmp((char *) &a, (char *) &b, 4));
return (strncmp((char *) &a, (char *) &b, 4));
}
int32
btchar8cmp(char *a, char *b)
{
return (strncmp(a, b, 8));
return (strncmp(a, b, 8));
}
int32
btchar16cmp(char *a, char *b)
{
return (strncmp(a, b, 16));
return (strncmp(a, b, 16));
}
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
bttextcmp(struct varlena *a, struct varlena *b)
bttextcmp(struct varlena * a, struct varlena * b)
{
int res;
unsigned char *ap, *bp;
int res;
unsigned char *ap,
*bp;
#ifdef USE_LOCALE
int la = VARSIZE(a) - VARHDRSZ;
int lb = VARSIZE(b) - VARHDRSZ;
ap = (unsigned char *) palloc (la + 1);
bp = (unsigned char *) palloc (lb + 1);
int la = VARSIZE(a) - VARHDRSZ;
int lb = VARSIZE(b) - VARHDRSZ;
memcpy(ap, VARDATA(a), la);
*(ap + la) = '\0';
memcpy(bp, VARDATA(b), lb);
*(bp + lb) = '\0';
ap = (unsigned char *) palloc(la + 1);
bp = (unsigned char *) palloc(lb + 1);
res = strcoll (ap, bp);
pfree (ap);
pfree (bp);
memcpy(ap, VARDATA(a), la);
*(ap + la) = '\0';
memcpy(bp, VARDATA(b), lb);
*(bp + lb) = '\0';
res = strcoll(ap, bp);
pfree(ap);
pfree(bp);
#else
int len = VARSIZE(a);
/* len is the length of the shorter of the two strings */
if ( len > VARSIZE(b) )
len = VARSIZE(b);
int len = VARSIZE(a);
len -= VARHDRSZ;
/* len is the length of the shorter of the two strings */
if (len > VARSIZE(b))
len = VARSIZE(b);
ap = (unsigned char *) VARDATA(a);
bp = (unsigned char *) VARDATA(b);
/*
* If the two strings differ in the first len bytes, or if they're
* the same in the first len bytes and they're both len bytes long,
* we're done.
*/
res = 0;
if (len > 0) {
do {
res = (int) (*ap++ - *bp++);
len--;
} while (res == 0 && len != 0);
}
len -= VARHDRSZ;
ap = (unsigned char *) VARDATA(a);
bp = (unsigned char *) VARDATA(b);
/*
* If the two strings differ in the first len bytes, or if they're the
* same in the first len bytes and they're both len bytes long, we're
* done.
*/
res = 0;
if (len > 0)
{
do
{
res = (int) (*ap++ - *bp++);
len--;
} while (res == 0 && len != 0);
}
#endif
if (res != 0 || VARSIZE(a) == VARSIZE(b))
return (res);
/*
* The two strings are the same in the first len bytes, and they
* are of different lengths.
*/
if (VARSIZE(a) < VARSIZE(b))
return (-1);
else
return (1);
if (res != 0 || VARSIZE(a) == VARSIZE(b))
return (res);
/*
* The two strings are the same in the first len bytes, and they are
* of different lengths.
*/
if (VARSIZE(a) < VARSIZE(b))
return (-1);
else
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--
* manage scans on btrees.
* manage scans on btrees.
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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
* 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.
* The routines and global variables in this file guarantee that all
* scans in the local address space stay correctly positioned. This
* 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.
* 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.
* The routines and global variables in this file guarantee that all
* scans in the local address space stay correctly positioned. This
* 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.
*
* 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
* 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
* relation, and the same page, as the update.
* 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
* 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
* relation, and the same page, as the update.
*
*-------------------------------------------------------------------------
*/
@ -32,83 +32,87 @@
#include <storage/bufpage.h>
#include <access/nbtree.h>
typedef struct BTScanListData {
IndexScanDesc btsl_scan;
struct BTScanListData *btsl_next;
} BTScanListData;
typedef struct BTScanListData
{
IndexScanDesc btsl_scan;
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 bool _bt_scantouched(IndexScanDesc scan, 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);
/*
* _bt_regscan() -- register a new scan.
* _bt_regscan() -- register a new scan.
*/
void
_bt_regscan(IndexScanDesc scan)
{
BTScanList new_el;
new_el = (BTScanList) palloc(sizeof(BTScanListData));
new_el->btsl_scan = scan;
new_el->btsl_next = BTScans;
BTScans = new_el;
BTScanList new_el;
new_el = (BTScanList) palloc(sizeof(BTScanListData));
new_el->btsl_scan = scan;
new_el->btsl_next = BTScans;
BTScans = new_el;
}
/*
* _bt_dropscan() -- drop a scan from the scan list
* _bt_dropscan() -- drop a scan from the scan list
*/
void
_bt_dropscan(IndexScanDesc scan)
{
BTScanList chk, last;
last = (BTScanList) NULL;
for (chk = BTScans;
chk != (BTScanList) NULL && chk->btsl_scan != scan;
chk = chk->btsl_next) {
last = chk;
}
if (chk == (BTScanList) NULL)
elog(WARN, "btree scan list trashed; can't find 0x%lx", scan);
if (last == (BTScanList) NULL)
BTScans = chk->btsl_next;
else
last->btsl_next = chk->btsl_next;
pfree (chk);
BTScanList chk,
last;
last = (BTScanList) NULL;
for (chk = BTScans;
chk != (BTScanList) NULL && chk->btsl_scan != scan;
chk = chk->btsl_next)
{
last = chk;
}
if (chk == (BTScanList) NULL)
elog(WARN, "btree scan list trashed; can't find 0x%lx", scan);
if (last == (BTScanList) NULL)
BTScans = chk->btsl_next;
else
last->btsl_next = chk->btsl_next;
pfree(chk);
}
/*
* _bt_adjscans() -- adjust all scans in the scan list to compensate
* for a given deletion or insertion
* _bt_adjscans() -- adjust all scans in the scan list to compensate
* for a given deletion or insertion
*/
void
_bt_adjscans(Relation rel, ItemPointer tid, int op)
{
BTScanList l;
Oid relid;
relid = rel->rd_id;
for (l = BTScans; l != (BTScanList) NULL; l = l->btsl_next) {
if (relid == l->btsl_scan->relation->rd_id)
_bt_scandel(l->btsl_scan, op,
ItemPointerGetBlockNumber(tid),
ItemPointerGetOffsetNumber(tid));
}
BTScanList l;
Oid relid;
relid = rel->rd_id;
for (l = BTScans; l != (BTScanList) NULL; l = l->btsl_next)
{
if (relid == l->btsl_scan->relation->rd_id)
_bt_scandel(l->btsl_scan, op,
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
* 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
* adjust the scan in the following ways:
*
@ -126,80 +130,85 @@ _bt_adjscans(Relation rel, ItemPointer tid, int op)
static void
_bt_scandel(IndexScanDesc scan, int op, BlockNumber blkno, OffsetNumber offno)
{
ItemPointer current;
Buffer buf;
BTScanOpaque so;
if (!_bt_scantouched(scan, blkno, offno))
return;
so = (BTScanOpaque) scan->opaque;
buf = so->btso_curbuf;
current = &(scan->currentItemData);
if (ItemPointerIsValid(current)
&& ItemPointerGetBlockNumber(current) == blkno
&& ItemPointerGetOffsetNumber(current) >= offno) {
switch (op) {
case BT_INSERT:
_bt_step(scan, &buf, ForwardScanDirection);
break;
case BT_DELETE:
_bt_step(scan, &buf, BackwardScanDirection);
break;
default:
elog(WARN, "_bt_scandel: bad operation '%d'", op);
/*NOTREACHED*/
ItemPointer current;
Buffer buf;
BTScanOpaque so;
if (!_bt_scantouched(scan, blkno, offno))
return;
so = (BTScanOpaque) scan->opaque;
buf = so->btso_curbuf;
current = &(scan->currentItemData);
if (ItemPointerIsValid(current)
&& ItemPointerGetBlockNumber(current) == blkno
&& ItemPointerGetOffsetNumber(current) >= offno)
{
switch (op)
{
case BT_INSERT:
_bt_step(scan, &buf, ForwardScanDirection);
break;
case BT_DELETE:
_bt_step(scan, &buf, BackwardScanDirection);
break;
default:
elog(WARN, "_bt_scandel: bad operation '%d'", op);
/* NOTREACHED */
}
so->btso_curbuf = buf;
}
so->btso_curbuf = buf;
}
current = &(scan->currentMarkData);
if (ItemPointerIsValid(current)
&& ItemPointerGetBlockNumber(current) == blkno
&& ItemPointerGetOffsetNumber(current) >= offno) {
ItemPointerData tmp;
tmp = *current;
*current = scan->currentItemData;
scan->currentItemData = tmp;
switch (op) {
case BT_INSERT:
_bt_step(scan, &buf, ForwardScanDirection);
break;
case BT_DELETE:
_bt_step(scan, &buf, BackwardScanDirection);
break;
default:
elog(WARN, "_bt_scandel: bad operation '%d'", op);
/*NOTREACHED*/
current = &(scan->currentMarkData);
if (ItemPointerIsValid(current)
&& ItemPointerGetBlockNumber(current) == blkno
&& ItemPointerGetOffsetNumber(current) >= offno)
{
ItemPointerData tmp;
tmp = *current;
*current = scan->currentItemData;
scan->currentItemData = tmp;
switch (op)
{
case BT_INSERT:
_bt_step(scan, &buf, ForwardScanDirection);
break;
case BT_DELETE:
_bt_step(scan, &buf, BackwardScanDirection);
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
* change to the index
* _bt_scantouched() -- check to see if a scan is affected by a given
* change to the index
*/
static bool
static bool
_bt_scantouched(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno)
{
ItemPointer current;
current = &(scan->currentItemData);
if (ItemPointerIsValid(current)
&& ItemPointerGetBlockNumber(current) == blkno
&& ItemPointerGetOffsetNumber(current) >= offno)
return (true);
current = &(scan->currentMarkData);
if (ItemPointerIsValid(current)
&& ItemPointerGetBlockNumber(current) == blkno
&& ItemPointerGetOffsetNumber(current) >= offno)
return (true);
return (false);
ItemPointer current;
current = &(scan->currentItemData);
if (ItemPointerIsValid(current)
&& ItemPointerGetBlockNumber(current) == blkno
&& ItemPointerGetOffsetNumber(current) >= offno)
return (true);
current = &(scan->currentMarkData);
if (ItemPointerIsValid(current)
&& ItemPointerGetBlockNumber(current) == blkno
&& ItemPointerGetOffsetNumber(current) >= offno)
return (true);
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--
* 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
*
*
* 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:
* StrategyNegate, StrategyCommute, and StrategyNegateCommute
* assume <, <=, ==, >=, > ordering.
* StrategyNegate, StrategyCommute, and StrategyNegateCommute
* assume <, <=, ==, >=, > ordering.
*/
static StrategyNumber BTNegate[5] = {
BTGreaterEqualStrategyNumber,
BTGreaterStrategyNumber,
InvalidStrategy,
BTLessStrategyNumber,
BTLessEqualStrategyNumber
static StrategyNumber BTNegate[5] = {
BTGreaterEqualStrategyNumber,
BTGreaterStrategyNumber,
InvalidStrategy,
BTLessStrategyNumber,
BTLessEqualStrategyNumber
};
static StrategyNumber BTCommute[5] = {
BTGreaterStrategyNumber,
BTGreaterEqualStrategyNumber,
InvalidStrategy,
BTLessEqualStrategyNumber,
BTLessStrategyNumber
static StrategyNumber BTCommute[5] = {
BTGreaterStrategyNumber,
BTGreaterEqualStrategyNumber,
InvalidStrategy,
BTLessEqualStrategyNumber,
BTLessStrategyNumber
};
static StrategyNumber BTNegateCommute[5] = {
BTLessEqualStrategyNumber,
BTLessStrategyNumber,
InvalidStrategy,
BTGreaterStrategyNumber,
BTGreaterEqualStrategyNumber
static StrategyNumber BTNegateCommute[5] = {
BTLessEqualStrategyNumber,
BTLessStrategyNumber,
InvalidStrategy,
BTGreaterStrategyNumber,
BTGreaterEqualStrategyNumber
};
static uint16 BTLessTermData[] = { /* XXX type clash */
2,
BTLessStrategyNumber,
SK_NEGATE,
BTLessStrategyNumber,
SK_NEGATE | SK_COMMUTE
static uint16 BTLessTermData[] = { /* XXX type clash */
2,
BTLessStrategyNumber,
SK_NEGATE,
BTLessStrategyNumber,
SK_NEGATE | SK_COMMUTE
};
static uint16 BTLessEqualTermData[] = { /* XXX type clash */
2,
BTLessEqualStrategyNumber,
0x0,
BTLessEqualStrategyNumber,
SK_COMMUTE
static uint16 BTLessEqualTermData[] = { /* XXX type clash */
2,
BTLessEqualStrategyNumber,
0x0,
BTLessEqualStrategyNumber,
SK_COMMUTE
};
static uint16 BTGreaterEqualTermData[] = { /* XXX type clash */
2,
BTGreaterEqualStrategyNumber,
0x0,
BTGreaterEqualStrategyNumber,
SK_COMMUTE
};
static uint16 BTGreaterTermData[] = { /* XXX type clash */
2,
BTGreaterStrategyNumber,
SK_NEGATE,
BTGreaterStrategyNumber,
SK_NEGATE | SK_COMMUTE
2,
BTGreaterEqualStrategyNumber,
0x0,
BTGreaterEqualStrategyNumber,
SK_COMMUTE
};
static StrategyTerm BTEqualExpressionData[] = {
(StrategyTerm)BTLessTermData, /* XXX */
(StrategyTerm)BTLessEqualTermData, /* XXX */
(StrategyTerm)BTGreaterEqualTermData, /* XXX */
(StrategyTerm)BTGreaterTermData, /* XXX */
NULL
static uint16 BTGreaterTermData[] = { /* XXX type clash */
2,
BTGreaterStrategyNumber,
SK_NEGATE,
BTGreaterStrategyNumber,
SK_NEGATE | SK_COMMUTE
};
static StrategyEvaluationData BTEvaluationData = {
/* XXX static for simplicity */
BTMaxStrategyNumber,
(StrategyTransformMap)BTNegate, /* XXX */
(StrategyTransformMap)BTCommute, /* XXX */
(StrategyTransformMap)BTNegateCommute, /* XXX */
static StrategyTerm BTEqualExpressionData[] = {
(StrategyTerm) BTLessTermData, /* XXX */
(StrategyTerm) BTLessEqualTermData, /* XXX */
(StrategyTerm) BTGreaterEqualTermData, /* XXX */
(StrategyTerm) BTGreaterTermData, /* XXX */
NULL
};
{ NULL, NULL, (StrategyExpression)BTEqualExpressionData, NULL, NULL,
NULL,NULL,NULL,NULL,NULL,NULL,NULL}
static StrategyEvaluationData BTEvaluationData = {
/* XXX static for simplicity */
BTMaxStrategyNumber,
(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
_bt_getstrat(Relation rel,
AttrNumber attno,
RegProcedure proc)
AttrNumber attno,
RegProcedure proc)
{
StrategyNumber strat;
strat = RelationGetStrategy(rel, attno, &BTEvaluationData, proc);
Assert(StrategyNumberIsValid(strat));
return (strat);
StrategyNumber strat;
strat = RelationGetStrategy(rel, attno, &BTEvaluationData, proc);
Assert(StrategyNumberIsValid(strat));
return (strat);
}
bool
_bt_invokestrat(Relation rel,
AttrNumber attno,
StrategyNumber strat,
Datum left,
Datum right)
AttrNumber attno,
StrategyNumber strat,
Datum left,
Datum right)
{
return (RelationInvokeStrategy(rel, &BTEvaluationData, attno, strat,
left, right));
return (RelationInvokeStrategy(rel, &BTEvaluationData, attno, strat,
left, right));
}

View File

@ -1,13 +1,13 @@
/*-------------------------------------------------------------------------
*
* btutils.c--
* Utility code for Postgres btree implementation.
* Utility code for Postgres btree implementation.
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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 <executor/execdebug.h>
extern int NIndexTupleProcessed;
extern int NIndexTupleProcessed;
#ifndef HAVE_MEMMOVE
# include <regex/utils.h>
#include <regex/utils.h>
#else
# include <string.h>
#include <string.h>
#endif
ScanKey
ScanKey
_bt_mkscankey(Relation rel, IndexTuple itup)
{
ScanKey skey;
TupleDesc itupdesc;
int natts;
int i;
Datum arg;
RegProcedure proc;
bool null;
bits16 flag;
natts = rel->rd_rel->relnatts;
itupdesc = RelationGetTupleDescriptor(rel);
skey = (ScanKey) palloc(natts * sizeof(ScanKeyData));
for (i = 0; i < natts; i++) {
arg = index_getattr(itup, i + 1, itupdesc, &null);
if ( null )
{
ScanKey skey;
TupleDesc itupdesc;
int natts;
int i;
Datum arg;
RegProcedure proc;
bool null;
bits16 flag;
natts = rel->rd_rel->relnatts;
itupdesc = RelationGetTupleDescriptor(rel);
skey = (ScanKey) palloc(natts * sizeof(ScanKeyData));
for (i = 0; i < natts; i++)
{
proc = NullValueRegProcedure;
flag = SK_ISNULL;
arg = index_getattr(itup, i + 1, itupdesc, &null);
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
_bt_freeskey(ScanKey skey)
{
pfree(skey);
pfree(skey);
}
void
_bt_freestack(BTStack stack)
{
BTStack ostack;
while (stack != (BTStack) NULL) {
ostack = stack;
stack = stack->bts_parent;
pfree(ostack->bts_btitem);
pfree(ostack);
}
BTStack ostack;
while (stack != (BTStack) NULL)
{
ostack = stack;
stack = stack->bts_parent;
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 index. This routine only needs to be called if there are
* more than one qual clauses using this index.
* 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
* more than one qual clauses using this index.
*/
void
_bt_orderkeys(Relation relation, BTScanOpaque so)
{
ScanKey xform;
ScanKeyData *cur;
StrategyMap map;
int nbytes;
long test;
int i, j;
int init[BTMaxStrategyNumber+1];
ScanKey key;
uint16 numberOfKeys = so->numberOfKeys;
uint16 new_numberOfKeys = 0;
AttrNumber attno = 1;
if ( numberOfKeys < 1 )
return;
key = so->keyData;
cur = &key[0];
if ( cur->sk_attno != 1 )
elog (WARN, "_bt_orderkeys: key(s) for attribute 1 missed");
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);
ScanKey xform;
ScanKeyData *cur;
StrategyMap map;
int nbytes;
long test;
int i,
j;
int init[BTMaxStrategyNumber + 1];
ScanKey key;
uint16 numberOfKeys = so->numberOfKeys;
uint16 new_numberOfKeys = 0;
AttrNumber attno = 1;
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 (numberOfKeys < 1)
return;
if ( cur->sk_flags & SK_ISNULL ) /* see comments above */
so->qual_ok = 0;
key = so->keyData;
if ( i == numberOfKeys || cur->sk_attno != attno )
cur = &key[0];
if (cur->sk_attno != 1)
elog(WARN, "_bt_orderkeys: key(s) for attribute 1 missed");
if (numberOfKeys == 1)
{
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.
* 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
*/
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;
if (cur->sk_flags & SK_ISNULL)
so->qual_ok = 0;
so->numberOfFirstKeys = 1;
return;
}
/* 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++)
/* 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 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; )
{
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++;
}
so->numberOfKeys = new_numberOfKeys;
pfree(xform);
so->numberOfKeys = new_numberOfKeys;
pfree(xform);
}
BTItem
_bt_formitem(IndexTuple itup)
{
int nbytes_btitem;
BTItem btitem;
Size tuplen;
extern Oid newoid();
/* see comments in btbuild
if (itup->t_info & INDEX_NULL_MASK)
elog(WARN, "btree indices cannot include null keys");
*/
/* make a copy of the index tuple with room for the sequence number */
tuplen = IndexTupleSize(itup);
nbytes_btitem = tuplen +
(sizeof(BTItemData) - sizeof(IndexTupleData));
btitem = (BTItem) palloc(nbytes_btitem);
memmove((char *) &(btitem->bti_itup), (char *) itup, tuplen);
int nbytes_btitem;
BTItem btitem;
Size tuplen;
extern Oid newoid();
/*
* see comments in btbuild
*
* if (itup->t_info & INDEX_NULL_MASK) elog(WARN, "btree indices cannot
* include null keys");
*/
/* make a copy of the index tuple with room for the sequence number */
tuplen = IndexTupleSize(itup);
nbytes_btitem = tuplen +
(sizeof(BTItemData) - sizeof(IndexTupleData));
btitem = (BTItem) palloc(nbytes_btitem);
memmove((char *) &(btitem->bti_itup), (char *) itup, tuplen);
#ifndef BTREE_VERSION_1
btitem->bti_oid = newoid();
btitem->bti_oid = newoid();
#endif
return (btitem);
return (btitem);
}
#ifdef NOT_USED
bool
_bt_checkqual(IndexScanDesc scan, IndexTuple itup)
{
BTScanOpaque so;
so = (BTScanOpaque) scan->opaque;
if (so->numberOfKeys > 0)
return (index_keytest(itup, RelationGetTupleDescriptor(scan->relation),
so->numberOfKeys, so->keyData));
else
return (true);
BTScanOpaque so;
so = (BTScanOpaque) scan->opaque;
if (so->numberOfKeys > 0)
return (index_keytest(itup, RelationGetTupleDescriptor(scan->relation),
so->numberOfKeys, so->keyData));
else
return (true);
}
#endif
#ifdef NOT_USED
bool
_bt_checkforkeys(IndexScanDesc scan, IndexTuple itup, Size keysz)
{
BTScanOpaque so;
so = (BTScanOpaque) scan->opaque;
if ( keysz > 0 && so->numberOfKeys >= keysz )
return (index_keytest(itup, RelationGetTupleDescriptor(scan->relation),
keysz, so->keyData));
else
return (true);
BTScanOpaque so;
so = (BTScanOpaque) scan->opaque;
if (keysz > 0 && so->numberOfKeys >= keysz)
return (index_keytest(itup, RelationGetTupleDescriptor(scan->relation),
keysz, so->keyData));
else
return (true);
}
#endif
bool
_bt_checkkeys (IndexScanDesc scan, IndexTuple tuple, Size *keysok)
_bt_checkkeys(IndexScanDesc scan, IndexTuple tuple, Size * keysok)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
Size keysz = so->numberOfKeys;
TupleDesc tupdesc;
ScanKey key;
Datum datum;
bool isNull;
int test;
*keysok = 0;
if ( keysz == 0 )
return (true);
key = so->keyData;
tupdesc = RelationGetTupleDescriptor(scan->relation);
IncrIndexProcessed();
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 )
BTScanOpaque so = (BTScanOpaque) scan->opaque;
Size keysz = so->numberOfKeys;
TupleDesc tupdesc;
ScanKey key;
Datum datum;
bool isNull;
int test;
*keysok = 0;
if (keysz == 0)
return (true);
key = so->keyData;
tupdesc = RelationGetTupleDescriptor(scan->relation);
IncrIndexProcessed();
while (keysz > 0)
{
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) {
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);
return (true);
}

View File

@ -1,19 +1,19 @@
/*-------------------------------------------------------------------------
*
* rtget.c--
* fetch tuples from an rtree scan.
* fetch tuples from an rtree scan.
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#include <postgres.h>
#include <storage/bufmgr.h>
#include <access/sdir.h>
#include <access/relscan.h>
@ -21,14 +21,15 @@
#include <access/rtree.h>
#include <storage/bufpage.h>
#ifndef HAVE_MEMMOVE
# include <regex/utils.h>
#include <regex/utils.h>
#else
# include <string.h>
#include <string.h>
#endif
static OffsetNumber findnext(IndexScanDesc s, Page p, OffsetNumber n,
ScanDirection dir);
static OffsetNumber
findnext(IndexScanDesc s, Page p, OffsetNumber n,
ScanDirection dir);
static RetrieveIndexResult rtscancache(IndexScanDesc s, ScanDirection dir);
static RetrieveIndexResult rtfirst(IndexScanDesc s, ScanDirection dir);
static RetrieveIndexResult rtnext(IndexScanDesc s, ScanDirection dir);
@ -38,278 +39,315 @@ static ItemPointer rtheapptr(Relation r, ItemPointer itemp);
RetrieveIndexResult
rtgettuple(IndexScanDesc s, ScanDirection dir)
{
RetrieveIndexResult res;
/* if we have it cached in the scan desc, just return the value */
if ((res = rtscancache(s, dir)) != (RetrieveIndexResult) NULL)
RetrieveIndexResult res;
/* if we have it cached in the scan desc, just return the value */
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);
/* 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)
{
Buffer b;
Page p;
OffsetNumber n;
OffsetNumber maxoff;
RetrieveIndexResult res;
RTreePageOpaque po;
RTreeScanOpaque so;
RTSTACK *stk;
BlockNumber blk;
IndexTuple it;
b = ReadBuffer(s->relation, P_ROOT);
p = BufferGetPage(b);
po = (RTreePageOpaque) PageGetSpecialPointer(p);
so = (RTreeScanOpaque) s->opaque;
for (;;) {
maxoff = PageGetMaxOffsetNumber(p);
if (ScanDirectionIsBackward(dir))
n = findnext(s, p, maxoff, dir);
else
n = findnext(s, p, FirstOffsetNumber, 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);
po = (RTreePageOpaque) PageGetSpecialPointer(p);
maxoff = PageGetMaxOffsetNumber(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);
Buffer b;
Page p;
OffsetNumber n;
OffsetNumber maxoff;
RetrieveIndexResult res;
RTreePageOpaque po;
RTreeScanOpaque so;
RTSTACK *stk;
BlockNumber blk;
IndexTuple it;
b = ReadBuffer(s->relation, P_ROOT);
p = BufferGetPage(b);
po = (RTreePageOpaque) PageGetSpecialPointer(p);
so = (RTreeScanOpaque) s->opaque;
for (;;)
{
maxoff = PageGetMaxOffsetNumber(p);
if (ScanDirectionIsBackward(dir))
n = findnext(s, p, maxoff, dir);
else
n = findnext(s, p, FirstOffsetNumber, 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);
po = (RTreePageOpaque) PageGetSpecialPointer(p);
maxoff = PageGetMaxOffsetNumber(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)
{
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)
{
Buffer b;
Page p;
OffsetNumber n;
OffsetNumber maxoff;
RetrieveIndexResult res;
RTreePageOpaque po;
RTreeScanOpaque so;
RTSTACK *stk;
BlockNumber blk;
IndexTuple it;
blk = ItemPointerGetBlockNumber(&(s->currentItemData));
n = ItemPointerGetOffsetNumber(&(s->currentItemData));
if (ScanDirectionIsForward(dir)) {
n = OffsetNumberNext(n);
} else {
n = OffsetNumberPrev(n);
}
Buffer b;
Page p;
OffsetNumber n;
OffsetNumber maxoff;
RetrieveIndexResult res;
RTreePageOpaque po;
RTreeScanOpaque so;
RTSTACK *stk;
BlockNumber blk;
IndexTuple it;
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);
blk = ItemPointerGetBlockNumber(&(s->currentItemData));
n = ItemPointerGetOffsetNumber(&(s->currentItemData));
if (ScanDirectionIsForward(dir))
{
n = OffsetNumberNext(n);
}
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;
}
else
{
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)
{
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)
{
OffsetNumber maxoff;
IndexTuple it;
RTreePageOpaque po;
RTreeScanOpaque so;
maxoff = PageGetMaxOffsetNumber(p);
po = (RTreePageOpaque) PageGetSpecialPointer(p);
so = (RTreeScanOpaque) s->opaque;
/*
* 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.
*/
if (so->s_flags & RTS_CURBEFORE) {
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;
OffsetNumber maxoff;
IndexTuple it;
RTreePageOpaque po;
RTreeScanOpaque so;
maxoff = PageGetMaxOffsetNumber(p);
po = (RTreePageOpaque) PageGetSpecialPointer(p);
so = (RTreeScanOpaque) s->opaque;
/*
* 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.
*/
if (so->s_flags & RTS_CURBEFORE)
{
so->s_flags &= ~RTS_CURBEFORE;
n = OffsetNumberPrev(n);
}
if (ScanDirectionIsBackward(dir)) {
n = OffsetNumberPrev(n);
} else {
n = OffsetNumberNext(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))
{
n = OffsetNumberPrev(n);
}
else
{
n = OffsetNumberNext(n);
}
}
}
return (n);
return (n);
}
static RetrieveIndexResult
static RetrieveIndexResult
rtscancache(IndexScanDesc s, ScanDirection dir)
{
RetrieveIndexResult res;
ItemPointer ip;
if (!(ScanDirectionIsNoMovement(dir)
&& ItemPointerIsValid(&(s->currentItemData)))) {
return ((RetrieveIndexResult) NULL);
}
ip = rtheapptr(s->relation, &(s->currentItemData));
if (ItemPointerIsValid(ip))
res = FormRetrieveIndexResult(&(s->currentItemData), ip);
else
res = (RetrieveIndexResult) NULL;
pfree (ip);
RetrieveIndexResult res;
ItemPointer ip;
return (res);
if (!(ScanDirectionIsNoMovement(dir)
&& ItemPointerIsValid(&(s->currentItemData))))
{
return ((RetrieveIndexResult) NULL);
}
ip = rtheapptr(s->relation, &(s->currentItemData));
if (ItemPointerIsValid(ip))
res = FormRetrieveIndexResult(&(s->currentItemData), ip);
else
res = (RetrieveIndexResult) NULL;
pfree(ip);
return (res);
}
/*
* rtheapptr returns the item pointer to the tuple in the heap relation
* for which itemp is the index relation item pointer.
* rtheapptr returns the item pointer to the tuple in the heap relation
* for which itemp is the index relation item pointer.
*/
static ItemPointer
static ItemPointer
rtheapptr(Relation r, ItemPointer itemp)
{
Buffer b;
Page p;
IndexTuple it;
ItemPointer ip;
OffsetNumber n;
ip = (ItemPointer) palloc(sizeof(ItemPointerData));
if (ItemPointerIsValid(itemp)) {
b = ReadBuffer(r, ItemPointerGetBlockNumber(itemp));
p = BufferGetPage(b);
n = ItemPointerGetOffsetNumber(itemp);
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
memmove((char *) ip, (char *) &(it->t_tid),
sizeof(ItemPointerData));
ReleaseBuffer(b);
} else {
ItemPointerSetInvalid(ip);
}
return (ip);
Buffer b;
Page p;
IndexTuple it;
ItemPointer ip;
OffsetNumber n;
ip = (ItemPointer) palloc(sizeof(ItemPointerData));
if (ItemPointerIsValid(itemp))
{
b = ReadBuffer(r, ItemPointerGetBlockNumber(itemp));
p = BufferGetPage(b);
n = ItemPointerGetOffsetNumber(itemp);
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
memmove((char *) ip, (char *) &(it->t_tid),
sizeof(ItemPointerData));
ReleaseBuffer(b);
}
else
{
ItemPointerSetInvalid(ip);
}
return (ip);
}

View File

@ -1,13 +1,13 @@
/*-------------------------------------------------------------------------
*
* rtproc.c--
* pg_amproc entries for rtrees.
* pg_amproc entries for rtrees.
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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/geo_decls.h>
#ifndef HAVE_MEMMOVE
# include <regex/utils.h>
#include <regex/utils.h>
#else
# include <string.h>
#include <string.h>
#endif
BOX
*rt_box_union(BOX *a, BOX *b)
* rt_box_union(BOX * a, BOX * b)
{
BOX *n;
if ((n = (BOX *) palloc(sizeof (*n))) == (BOX *) NULL)
elog(WARN, "Cannot allocate box for union");
n->high.x = Max(a->high.x, b->high.x);
n->high.y = Max(a->high.y, b->high.y);
n->low.x = Min(a->low.x, b->low.x);
n->low.y = Min(a->low.y, b->low.y);
return (n);
BOX *n;
if ((n = (BOX *) palloc(sizeof(*n))) == (BOX *) NULL)
elog(WARN, "Cannot allocate box for union");
n->high.x = Max(a->high.x, b->high.x);
n->high.y = Max(a->high.y, b->high.y);
n->low.x = Min(a->low.x, b->low.x);
n->low.y = Min(a->low.y, b->low.y);
return (n);
}
BOX *
rt_box_inter(BOX *a, BOX *b)
BOX *
rt_box_inter(BOX * a, BOX * b)
{
BOX *n;
if ((n = (BOX *) palloc(sizeof (*n))) == (BOX *) NULL)
elog(WARN, "Cannot allocate box for union");
n->high.x = Min(a->high.x, b->high.x);
n->high.y = Min(a->high.y, b->high.y);
n->low.x = Max(a->low.x, b->low.x);
n->low.y = Max(a->low.y, b->low.y);
if (n->high.x < n->low.x || n->high.y < n->low.y) {
pfree(n);
return ((BOX *) NULL);
}
return (n);
BOX *n;
if ((n = (BOX *) palloc(sizeof(*n))) == (BOX *) NULL)
elog(WARN, "Cannot allocate box for union");
n->high.x = Min(a->high.x, b->high.x);
n->high.y = Min(a->high.y, b->high.y);
n->low.x = Max(a->low.x, b->low.x);
n->low.y = Max(a->low.y, b->low.y);
if (n->high.x < n->low.x || n->high.y < n->low.y)
{
pfree(n);
return ((BOX *) NULL);
}
return (n);
}
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)
*size = 0.0;
else
*size = (float) ((a->high.x - a->low.x) * (a->high.y - a->low.y));
return;
if (a == (BOX *) NULL || a->high.x <= a->low.x || a->high.y <= a->low.y)
*size = 0.0;
else
*size = (float) ((a->high.x - a->low.x) * (a->high.y - a->low.y));
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
* 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
* have a special return type for big boxes.
* In an earlier release of the system, this routine did something
* 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
* have a special return type for big boxes.
*/
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 *
rt_poly_union(POLYGON *a, POLYGON *b)
POLYGON *
rt_poly_union(POLYGON * a, POLYGON * b)
{
POLYGON *p;
p = (POLYGON *)PALLOCTYPE(POLYGON);
if (!PointerIsValid(p))
elog(WARN, "Cannot allocate polygon for union");
memset((char *) p, 0, sizeof(POLYGON)); /* zero any holes */
p->size = sizeof(POLYGON);
p->npts = 0;
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.low.x = Min(a->boundbox.low.x, b->boundbox.low.x);
p->boundbox.low.y = Min(a->boundbox.low.y, b->boundbox.low.y);
return p;
POLYGON *p;
p = (POLYGON *) PALLOCTYPE(POLYGON);
if (!PointerIsValid(p))
elog(WARN, "Cannot allocate polygon for union");
memset((char *) p, 0, sizeof(POLYGON)); /* zero any holes */
p->size = sizeof(POLYGON);
p->npts = 0;
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.low.x = Min(a->boundbox.low.x, b->boundbox.low.x);
p->boundbox.low.y = Min(a->boundbox.low.y, b->boundbox.low.y);
return p;
}
void
rt_poly_size(POLYGON *a, float *size)
rt_poly_size(POLYGON * a, float *size)
{
double xdim, ydim;
size = (float *) palloc(sizeof(float));
if (a == (POLYGON *) NULL ||
a->boundbox.high.x <= a->boundbox.low.x ||
a->boundbox.high.y <= a->boundbox.low.y)
*size = 0.0;
else {
xdim = (a->boundbox.high.x - a->boundbox.low.x);
ydim = (a->boundbox.high.y - a->boundbox.low.y);
*size = (float) (xdim * ydim);
}
return;
double xdim,
ydim;
size = (float *) palloc(sizeof(float));
if (a == (POLYGON *) NULL ||
a->boundbox.high.x <= a->boundbox.low.x ||
a->boundbox.high.y <= a->boundbox.low.y)
*size = 0.0;
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 *
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)
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);
pfree(p);
return ((POLYGON *) NULL);
}
return (p);
return (p);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,19 +1,19 @@
/*-------------------------------------------------------------------------
*
* 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
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#include <postgres.h>
#include <storage/bufmgr.h>
#include <access/genam.h>
#include <storage/lmgr.h>
@ -21,377 +21,411 @@
#include <access/rtree.h>
#include <access/rtstrat.h>
#ifndef HAVE_MEMMOVE
# include <regex/utils.h>
#include <regex/utils.h>
#else
# include <string.h>
#include <string.h>
#endif
/* routines defined and used here */
static void rtregscan(IndexScanDesc s);
static void rtdropscan(IndexScanDesc s);
static void rtadjone(IndexScanDesc s, int op, BlockNumber blkno,
OffsetNumber offnum);
static void adjuststack(RTSTACK *stk, BlockNumber blkno,
static void rtregscan(IndexScanDesc s);
static void rtdropscan(IndexScanDesc s);
static void
rtadjone(IndexScanDesc s, int op, BlockNumber blkno,
OffsetNumber offnum);
static void
adjuststack(RTSTACK * stk, BlockNumber blkno,
OffsetNumber offnum);
static void adjustiptr(IndexScanDesc s, ItemPointer iptr,
int op, BlockNumber blkno, OffsetNumber offnum);
static void
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
* 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
* 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
* 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.
* 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
* 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
* 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
* locks on the same object, so that's why we need to handle this case.
*/
typedef struct RTScanListData {
IndexScanDesc rtsl_scan;
struct RTScanListData *rtsl_next;
} RTScanListData;
typedef struct RTScanListData
{
IndexScanDesc rtsl_scan;
struct RTScanListData *rtsl_next;
} RTScanListData;
typedef RTScanListData *RTScanList;
typedef RTScanListData *RTScanList;
/* pointer to list of local scans on rtrees */
static RTScanList RTScans = (RTScanList) NULL;
IndexScanDesc
rtbeginscan(Relation r,
bool fromEnd,
uint16 nkeys,
ScanKey key)
bool fromEnd,
uint16 nkeys,
ScanKey key)
{
IndexScanDesc s;
RelationSetLockForRead(r);
s = RelationGetIndexScan(r, fromEnd, nkeys, key);
rtregscan(s);
return (s);
IndexScanDesc s;
RelationSetLockForRead(r);
s = RelationGetIndexScan(r, fromEnd, nkeys, key);
rtregscan(s);
return (s);
}
void
rtrescan(IndexScanDesc s, bool fromEnd, ScanKey key)
{
RTreeScanOpaque p;
RegProcedure internal_proc;
int i;
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++)
RTreeScanOpaque p;
RegProcedure internal_proc;
int i;
if (!IndexScanIsValid(s))
{
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
* 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);
}
/*
* 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;
}
}
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
rtmarkpos(IndexScanDesc s)
{
RTreeScanOpaque p;
RTSTACK *o, *n, *tmp;
s->currentMarkData = s->currentItemData;
p = (RTreeScanOpaque) s->opaque;
if (p->s_flags & RTS_CURBEFORE)
p->s_flags |= RTS_MRKBEFORE;
else
p->s_flags &= ~RTS_MRKBEFORE;
o = (RTSTACK *) NULL;
n = p->s_stack;
/* copy the parent stack from the current item data */
while (n != (RTSTACK *) NULL) {
tmp = (RTSTACK *) palloc(sizeof(RTSTACK));
tmp->rts_child = n->rts_child;
tmp->rts_blk = n->rts_blk;
tmp->rts_parent = o;
o = tmp;
n = n->rts_parent;
}
freestack(p->s_markstk);
p->s_markstk = o;
RTreeScanOpaque p;
RTSTACK *o,
*n,
*tmp;
s->currentMarkData = s->currentItemData;
p = (RTreeScanOpaque) s->opaque;
if (p->s_flags & RTS_CURBEFORE)
p->s_flags |= RTS_MRKBEFORE;
else
p->s_flags &= ~RTS_MRKBEFORE;
o = (RTSTACK *) NULL;
n = p->s_stack;
/* copy the parent stack from the current item data */
while (n != (RTSTACK *) NULL)
{
tmp = (RTSTACK *) palloc(sizeof(RTSTACK));
tmp->rts_child = n->rts_child;
tmp->rts_blk = n->rts_blk;
tmp->rts_parent = o;
o = tmp;
n = n->rts_parent;
}
freestack(p->s_markstk);
p->s_markstk = o;
}
void
rtrestrpos(IndexScanDesc s)
{
RTreeScanOpaque p;
RTSTACK *o, *n, *tmp;
s->currentItemData = s->currentMarkData;
p = (RTreeScanOpaque) s->opaque;
if (p->s_flags & RTS_MRKBEFORE)
p->s_flags |= RTS_CURBEFORE;
else
p->s_flags &= ~RTS_CURBEFORE;
o = (RTSTACK *) NULL;
n = p->s_markstk;
/* copy the parent stack from the current item data */
while (n != (RTSTACK *) NULL) {
tmp = (RTSTACK *) palloc(sizeof(RTSTACK));
tmp->rts_child = n->rts_child;
tmp->rts_blk = n->rts_blk;
tmp->rts_parent = o;
o = tmp;
n = n->rts_parent;
}
freestack(p->s_stack);
p->s_stack = o;
RTreeScanOpaque p;
RTSTACK *o,
*n,
*tmp;
s->currentItemData = s->currentMarkData;
p = (RTreeScanOpaque) s->opaque;
if (p->s_flags & RTS_MRKBEFORE)
p->s_flags |= RTS_CURBEFORE;
else
p->s_flags &= ~RTS_CURBEFORE;
o = (RTSTACK *) NULL;
n = p->s_markstk;
/* copy the parent stack from the current item data */
while (n != (RTSTACK *) NULL)
{
tmp = (RTSTACK *) palloc(sizeof(RTSTACK));
tmp->rts_child = n->rts_child;
tmp->rts_blk = n->rts_blk;
tmp->rts_parent = o;
o = tmp;
n = n->rts_parent;
}
freestack(p->s_stack);
p->s_stack = o;
}
void
rtendscan(IndexScanDesc s)
{
RTreeScanOpaque p;
p = (RTreeScanOpaque) s->opaque;
if (p != (RTreeScanOpaque) NULL) {
freestack(p->s_stack);
freestack(p->s_markstk);
pfree (s->opaque);
}
rtdropscan(s);
/* XXX don't unset read lock -- two-phase locking */
RTreeScanOpaque p;
p = (RTreeScanOpaque) s->opaque;
if (p != (RTreeScanOpaque) NULL)
{
freestack(p->s_stack);
freestack(p->s_markstk);
pfree(s->opaque);
}
rtdropscan(s);
/* XXX don't unset read lock -- two-phase locking */
}
static void
rtregscan(IndexScanDesc s)
{
RTScanList l;
l = (RTScanList) palloc(sizeof(RTScanListData));
l->rtsl_scan = s;
l->rtsl_next = RTScans;
RTScans = l;
RTScanList l;
l = (RTScanList) palloc(sizeof(RTScanListData));
l->rtsl_scan = s;
l->rtsl_next = RTScans;
RTScans = l;
}
static void
rtdropscan(IndexScanDesc s)
{
RTScanList l;
RTScanList prev;
prev = (RTScanList) NULL;
for (l = RTScans;
l != (RTScanList) NULL && l->rtsl_scan != s;
l = l->rtsl_next) {
prev = l;
}
if (l == (RTScanList) NULL)
elog(WARN, "rtree scan list corrupted -- cannot find 0x%lx", s);
if (prev == (RTScanList) NULL)
RTScans = l->rtsl_next;
else
prev->rtsl_next = l->rtsl_next;
pfree(l);
RTScanList l;
RTScanList prev;
prev = (RTScanList) NULL;
for (l = RTScans;
l != (RTScanList) NULL && l->rtsl_scan != s;
l = l->rtsl_next)
{
prev = l;
}
if (l == (RTScanList) NULL)
elog(WARN, "rtree scan list corrupted -- cannot find 0x%lx", s);
if (prev == (RTScanList) NULL)
RTScans = l->rtsl_next;
else
prev->rtsl_next = l->rtsl_next;
pfree(l);
}
void
rtadjscans(Relation r, int op, BlockNumber blkno, OffsetNumber offnum)
{
RTScanList l;
Oid relid;
relid = r->rd_id;
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);
}
RTScanList l;
Oid relid;
relid = r->rd_id;
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);
}
}
/*
* 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
* us what the modification is, and blkno and offind tell us what
* block and offset index were affected. This routine checks the
* current and marked positions, and the current and marked stacks,
* to see if any stored location needs to be changed because of the
* update. If so, we make the change here.
* 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
* block and offset index were affected. This routine checks the
* current and marked positions, and the current and marked stacks,
* to see if any stored location needs to be changed because of the
* update. If so, we make the change here.
*/
static void
rtadjone(IndexScanDesc s,
int op,
BlockNumber blkno,
OffsetNumber offnum)
int op,
BlockNumber blkno,
OffsetNumber offnum)
{
RTreeScanOpaque so;
adjustiptr(s, &(s->currentItemData), op, blkno, offnum);
adjustiptr(s, &(s->currentMarkData), op, blkno, offnum);
so = (RTreeScanOpaque) s->opaque;
if (op == RTOP_SPLIT) {
adjuststack(so->s_stack, blkno, offnum);
adjuststack(so->s_markstk, blkno, offnum);
}
RTreeScanOpaque so;
adjustiptr(s, &(s->currentItemData), op, blkno, offnum);
adjustiptr(s, &(s->currentMarkData), op, blkno, offnum);
so = (RTreeScanOpaque) s->opaque;
if (op == RTOP_SPLIT)
{
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
* need to do nothing, to back up one record, or to start over on
* the same page.
* 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
* the same page.
*/
static void
adjustiptr(IndexScanDesc s,
ItemPointer iptr,
int op,
BlockNumber blkno,
OffsetNumber offnum)
ItemPointer iptr,
int op,
BlockNumber blkno,
OffsetNumber offnum)
{
OffsetNumber curoff;
RTreeScanOpaque so;
if (ItemPointerIsValid(iptr)) {
if (ItemPointerGetBlockNumber(iptr) == blkno) {
curoff = ItemPointerGetOffsetNumber(iptr);
so = (RTreeScanOpaque) s->opaque;
switch (op) {
case RTOP_DEL:
/* back up one if we need to */
if (curoff >= offnum) {
if (curoff > FirstOffsetNumber) {
/* just adjust the item pointer */
ItemPointerSet(iptr, blkno, OffsetNumberPrev(curoff));
} else {
/* remember that we're before the current tuple */
ItemPointerSet(iptr, blkno, FirstOffsetNumber);
if (iptr == &(s->currentItemData))
so->s_flags |= RTS_CURBEFORE;
else
so->s_flags |= RTS_MRKBEFORE;
}
OffsetNumber curoff;
RTreeScanOpaque so;
if (ItemPointerIsValid(iptr))
{
if (ItemPointerGetBlockNumber(iptr) == blkno)
{
curoff = ItemPointerGetOffsetNumber(iptr);
so = (RTreeScanOpaque) s->opaque;
switch (op)
{
case RTOP_DEL:
/* back up one if we need to */
if (curoff >= offnum)
{
if (curoff > FirstOffsetNumber)
{
/* just adjust the item pointer */
ItemPointerSet(iptr, blkno, OffsetNumberPrev(curoff));
}
else
{
/* remember that we're before the current tuple */
ItemPointerSet(iptr, blkno, FirstOffsetNumber);
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
* the index we're scanning.
* adjuststack() -- adjust the supplied stack for a split on a page in
* the index we're scanning.
*
* 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
* 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
* 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
* are looking at already in this transaction, we ignore the update
* request.
* 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
* 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
* 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
* are looking at already in this transaction, we ignore the update
* request.
*/
/*ARGSUSED*/
static void
adjuststack(RTSTACK *stk,
BlockNumber blkno,
OffsetNumber offnum)
adjuststack(RTSTACK * stk,
BlockNumber blkno,
OffsetNumber offnum)
{
while (stk != (RTSTACK *) NULL) {
if (stk->rts_blk == blkno)
stk->rts_child = FirstOffsetNumber;
stk = stk->rts_parent;
}
while (stk != (RTSTACK *) NULL)
{
if (stk->rts_blk == blkno)
stk->rts_child = FirstOffsetNumber;
stk = stk->rts_parent;
}
}

View File

@ -1,241 +1,243 @@
/*-------------------------------------------------------------------------
*
* rtstrat.c--
* strategy map data for rtrees.
* strategy map data for rtrees.
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#include <postgres.h>
#include <utils/rel.h>
#include <access/rtree.h>
#include <access/istrat.h>
static StrategyNumber RelationGetRTStrategy(Relation r,
AttrNumber attnum, RegProcedure proc);
static StrategyNumber
RelationGetRTStrategy(Relation r,
AttrNumber attnum, RegProcedure proc);
/*
* Note: negate, commute, and negatecommute all assume that operators are
* ordered as follows in the strategy map:
* Note: negate, commute, and negatecommute all assume that operators are
* ordered as follows in the strategy map:
*
* left, left-or-overlap, overlap, right-or-overlap, right, same,
* contains, contained-by
* left, left-or-overlap, overlap, right-or-overlap, right, same,
* contains, contained-by
*
* The negate, commute, and negatecommute arrays are used by the planner
* to plan indexed scans over data that appears in the qualificiation in
* a boolean negation, or whose operands appear in the wrong order. For
* example, if the operator "<%" means "contains", and the user says
* The negate, commute, and negatecommute arrays are used by the planner
* to plan indexed scans over data that appears in the qualificiation in
* a boolean negation, or whose operands appear in the wrong order. For
* 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
* an operator in their operator class for negating <%.
* the planner can plan an index scan by noting that rtree indices have
* 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
* its opclass for commuting <%, and plan the scan using that operator.
* This added complexity in the access methods makes the planner a lot easier
* to write.
* 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.
* This added complexity in the access methods makes the planner a lot easier
* to write.
*/
/* if a op b, what operator tells us if (not a op b)? */
static StrategyNumber RTNegate[RTNStrategies] = {
InvalidStrategy,
InvalidStrategy,
InvalidStrategy,
InvalidStrategy,
InvalidStrategy,
InvalidStrategy,
InvalidStrategy,
InvalidStrategy
};
static StrategyNumber RTNegate[RTNStrategies] = {
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? */
static StrategyNumber RTCommute[RTNStrategies] = {
InvalidStrategy,
InvalidStrategy,
InvalidStrategy,
InvalidStrategy,
InvalidStrategy,
InvalidStrategy,
InvalidStrategy,
InvalidStrategy
};
static StrategyNumber RTCommute[RTNStrategies] = {
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)? */
static StrategyNumber RTNegateCommute[RTNStrategies] = {
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
}
static StrategyNumber RTNegateCommute[RTNStrategies] = {
InvalidStrategy,
InvalidStrategy,
InvalidStrategy,
InvalidStrategy,
InvalidStrategy,
InvalidStrategy,
InvalidStrategy,
InvalidStrategy
};
/*
* 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.
* 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.
*
* This array maps leaf search operators to the internal search operators.
* We assume the normal ordering on operators:
* 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.
*
* left, left-or-overlap, overlap, right-or-overlap, right, same,
* contains, contained-by
* 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
* 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] = {
RTOverLeftStrategyNumber,
RTOverLeftStrategyNumber,
RTOverlapStrategyNumber,
RTOverRightStrategyNumber,
RTOverRightStrategyNumber,
RTContainsStrategyNumber,
RTContainsStrategyNumber,
RTOverlapStrategyNumber
};
RTOverLeftStrategyNumber,
RTOverLeftStrategyNumber,
RTOverlapStrategyNumber,
RTOverRightStrategyNumber,
RTOverRightStrategyNumber,
RTContainsStrategyNumber,
RTContainsStrategyNumber,
RTOverlapStrategyNumber
};
static StrategyNumber
static StrategyNumber
RelationGetRTStrategy(Relation r,
AttrNumber attnum,
RegProcedure proc)
AttrNumber attnum,
RegProcedure proc)
{
return (RelationGetStrategy(r, attnum, &RTEvaluationData, proc));
return (RelationGetStrategy(r, attnum, &RTEvaluationData, proc));
}
#ifdef NOT_USED
bool
RelationInvokeRTStrategy(Relation r,
AttrNumber attnum,
StrategyNumber s,
Datum left,
Datum right)
AttrNumber attnum,
StrategyNumber s,
Datum left,
Datum right)
{
return (RelationInvokeStrategy(r, &RTEvaluationData, attnum, s,
left, right));
return (RelationInvokeStrategy(r, &RTEvaluationData, attnum, s,
left, right));
}
#endif
RegProcedure
RTMapOperator(Relation r,
AttrNumber attnum,
RegProcedure proc)
AttrNumber attnum,
RegProcedure proc)
{
StrategyNumber procstrat;
StrategyMap strategyMap;
procstrat = RelationGetRTStrategy(r, attnum, proc);
strategyMap = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(r),
RTNStrategies,
attnum);
return (strategyMap->entry[RTOperMap[procstrat - 1] - 1].sk_procedure);
StrategyNumber procstrat;
StrategyMap strategyMap;
procstrat = RelationGetRTStrategy(r, attnum, proc);
strategyMap = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(r),
RTNStrategies,
attnum);
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--
* POSTGRES transaction identifier code.
* POSTGRES transaction identifier code.
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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
* XXX WARNING
* Much of this file will change when we change our representation
* of transaction ids -cim 3/23/90
* Much of this file will change when we change our representation
* of transaction ids -cim 3/23/90
*
* It is time to make the switch from 5 byte to 4 byte transaction ids
* This file was totally reworked. -mer 5/22/92
@ -31,127 +31,127 @@ extern TransactionId AmiTransactionId;
extern TransactionId FirstTransactionId;
/* ----------------------------------------------------------------
* TransactionIdIsValid
* TransactionIdIsValid
*
* Macro-ize me.
* Macro-ize me.
* ----------------------------------------------------------------
*/
bool
TransactionIdIsValid(TransactionId transactionId)
{
return ((bool) (transactionId != NullTransactionId) );
return ((bool) (transactionId != NullTransactionId));
}
/* XXX char16 name for catalogs */
TransactionId
xidin(char *representation)
{
return (atol(representation));
return (atol(representation));
}
/* XXX char16 name for catalogs */
char*
char *
xidout(TransactionId transactionId)
{
/* return(TransactionIdFormString(transactionId)); */
char *representation;
/* maximum 32 bit unsigned integer representation takes 10 chars */
representation = palloc(11);
sprintf(representation, "%u", transactionId);
return (representation);
/* return(TransactionIdFormString(transactionId)); */
char *representation;
/* maximum 32 bit unsigned integer representation takes 10 chars */
representation = palloc(11);
sprintf(representation, "%u", transactionId);
return (representation);
}
/* ----------------------------------------------------------------
* StoreInvalidTransactionId
* StoreInvalidTransactionId
*
* Maybe do away with Pointer types in these routines.
* Macro-ize this one.
* Maybe do away with Pointer types in these routines.
* Macro-ize this one.
* ----------------------------------------------------------------
*/
void
StoreInvalidTransactionId(TransactionId *destination)
StoreInvalidTransactionId(TransactionId * destination)
{
*destination = NullTransactionId;
*destination = NullTransactionId;
}
/* ----------------------------------------------------------------
* TransactionIdStore
* TransactionIdStore
*
* Macro-ize this one.
* Macro-ize this one.
* ----------------------------------------------------------------
*/
void
TransactionIdStore(TransactionId transactionId,
TransactionId *destination)
TransactionId * destination)
{
*destination = transactionId;
*destination = transactionId;
}
/* ----------------------------------------------------------------
* TransactionIdEquals
* TransactionIdEquals
* ----------------------------------------------------------------
*/
bool
TransactionIdEquals(TransactionId id1, TransactionId id2)
{
return ((bool) (id1 == id2));
return ((bool) (id1 == id2));
}
/* ----------------------------------------------------------------
* TransactionIdIsLessThan
* TransactionIdIsLessThan
* ----------------------------------------------------------------
*/
bool
TransactionIdIsLessThan(TransactionId id1, TransactionId id2)
{
return ((bool)(id1 < id2));
return ((bool) (id1 < id2));
}
/* ----------------------------------------------------------------
* xideq
* xideq
* ----------------------------------------------------------------
*/
/*
* xideq - returns 1, iff xid1 == xid2
* 0 else;
* xideq - returns 1, iff xid1 == xid2
* 0 else;
*/
bool
xideq(TransactionId xid1, TransactionId xid2)
{
return( (bool) (xid1 == xid2) );
return ((bool) (xid1 == xid2));
}
/* ----------------------------------------------------------------
* TransactionIdIncrement
* TransactionIdIncrement
* ----------------------------------------------------------------
*/
#ifdef NOT_USED
void
TransactionIdIncrement(TransactionId *transactionId)
TransactionIdIncrement(TransactionId * transactionId)
{
(*transactionId)++;
if (*transactionId == DisabledTransactionId)
elog(FATAL, "TransactionIdIncrement: exhausted XID's");
return;
(*transactionId)++;
if (*transactionId == DisabledTransactionId)
elog(FATAL, "TransactionIdIncrement: exhausted XID's");
return;
}
#endif
/* ----------------------------------------------------------------
* TransactionIdAdd
* TransactionIdAdd
* ----------------------------------------------------------------
*/
void
TransactionIdAdd(TransactionId *xid, int value)
TransactionIdAdd(TransactionId * xid, int value)
{
*xid += value;
return;
*xid += value;
return;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,13 @@
/*-------------------------------------------------------------------------
*
* catalog.c--
*
*
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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 <miscadmin.h> /* for DataDir */
#include <miscadmin.h> /* for DataDir */
#include <utils/syscache.h>
#include <catalog/catname.h> /* NameIs{,Shared}SystemRelationName */
#include <catalog/pg_type.h>
@ -23,175 +23,188 @@
#include <access/transam.h>
/*
* relpath - path to the relation
* Perhaps this should be in-line code in relopen().
* relpath - path to the relation
* Perhaps this should be in-line code in relopen().
*/
char *
char *
relpath(char relname[])
{
char *path;
if (IsSharedSystemRelationName(relname)) {
path = (char *) palloc(strlen(DataDir) + sizeof(NameData) + 2);
sprintf(path, "%s/%s", DataDir, relname);
return (path);
}
return(relname);
char *path;
if (IsSharedSystemRelationName(relname))
{
path = (char *) palloc(strlen(DataDir) + sizeof(NameData) + 2);
sprintf(path, "%s/%s", DataDir, relname);
return (path);
}
return (relname);
}
#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
* with pg_ while user relns are forbidden to do so. Make the test
* trivial and instantaneous.
* 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
* trivial and instantaneous.
*
* XXX this is way bogus. -- pma
* XXX this is way bogus. -- pma
*/
bool
issystem(char relname[])
{
if (relname[0] && relname[1] && relname[2])
return (relname[0] == 'p' &&
relname[1] == 'g' &&
relname[2] == '_');
else
return FALSE;
if (relname[0] && relname[1] && relname[2])
return (relname[0] == 'p' &&
relname[1] == 'g' &&
relname[2] == '_');
else
return FALSE;
}
#endif
/*
* 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
* with pg_ while user relns are forbidden to do so. Make the test
* trivial and instantaneous.
* 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
* trivial and instantaneous.
*
* XXX this is way bogus. -- pma
* XXX this is way bogus. -- pma
*/
bool
IsSystemRelationName(char *relname)
{
if (relname[0] && relname[1] && relname[2])
return (relname[0] == 'p' &&
relname[1] == 'g' &&
relname[2] == '_');
else
return FALSE;
if (relname[0] && relname[1] && relname[2])
return (relname[0] == 'p' &&
relname[1] == 'g' &&
relname[2] == '_');
else
return FALSE;
}
/*
* 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
IsSharedSystemRelationName(char *relname)
{
int i;
/*
* Quick out: if it's not a system relation, it can't be a shared
* system relation.
*/
if (!IsSystemRelationName(relname))
int i;
/*
* Quick out: if it's not a system relation, it can't be a shared
* system relation.
*/
if (!IsSystemRelationName(relname))
return FALSE;
i = 0;
while (SharedSystemRelationNames[i] != NULL)
{
if (strcmp(SharedSystemRelationNames[i], relname) == 0)
return TRUE;
i++;
}
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
* access/transam/varsup.c. oids are now allocated correctly.
* Object Id allocation is now done by GetNewObjectID in
* access/transam/varsup.c. oids are now allocated correctly.
*
* old comments:
* This needs to change soon, it fails if there are too many more
* than one call per second when postgres restarts after it dies.
* This needs to change soon, it fails if there are too many more
* than one call per second when postgres restarts after it dies.
*
* The distribution of OID's should be done by the POSTMASTER.
* 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
* user programs to use them for temporary object identifiers.
* The distribution of OID's should be done by the POSTMASTER.
* 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
* user programs to use them for temporary object identifiers.
*/
Oid newoid()
Oid
newoid()
{
Oid lastoid;
GetNewObjectId(&lastoid);
if (! OidIsValid(lastoid))
elog(WARN, "newoid: GetNewObjectId returns invalid oid");
return lastoid;
Oid lastoid;
GetNewObjectId(&lastoid);
if (!OidIsValid(lastoid))
elog(WARN, "newoid: GetNewObjectId returns invalid oid");
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[].
* Returns with the attnum, and attlen domains set.
* attnum, attproc, atttyparg, ... should be set by the user.
* Expects that the atttypid domain is set for each att[].
* Returns with the attnum, and attlen domains set.
* 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
* information if this is at all possible.
* Current implementation is very inefficient--should cashe the
* information if this is at all possible.
*
* Check to see if this is really needed, and especially in the case
* of index tuples.
* Check to see if this is really needed, and especially in the case
* of index tuples.
*/
void
fillatt(TupleDesc tupleDesc)
{
AttributeTupleForm *attributeP;
register TypeTupleForm typp;
HeapTuple tuple;
int i;
int natts = tupleDesc->natts;
AttributeTupleForm *att = tupleDesc->attrs;
AttributeTupleForm *attributeP;
register TypeTupleForm typp;
HeapTuple tuple;
int i;
int natts = tupleDesc->natts;
AttributeTupleForm *att = tupleDesc->attrs;
if (natts < 0 || natts > MaxHeapAttributeNumber)
elog(WARN, "fillatt: %d attributes is too large", natts);
if (natts == 0) {
elog(DEBUG, "fillatt: called with natts == 0");
return;
}
attributeP = &att[0];
for (i = 0; i < natts;) {
tuple = SearchSysCacheTuple(TYPOID,
Int32GetDatum((*attributeP)->atttypid),
0,0,0);
if (!HeapTupleIsValid(tuple)) {
elog(WARN, "fillatt: unknown atttypid %ld",
(*attributeP)->atttypid);
} else {
(*attributeP)->attnum = (int16) ++i;
/* Check if the attr is a set before messing with the length
and byval, since those were already set in
TupleDescInitEntry. In fact, this seems redundant
here, but who knows what I'll break if I take it out...
same for char() and varchar() stuff. I share the same
sentiments. This function is poorly written anyway. -ay 6/95
*/
if (!(*attributeP)->attisset &&
(*attributeP)->atttypid!=BPCHAROID &&
(*attributeP)->atttypid!=VARCHAROID) {
typp = (TypeTupleForm) GETSTRUCT(tuple); /* XXX */
(*attributeP)->attlen = typp->typlen;
(*attributeP)->attbyval = typp->typbyval;
}
if (natts < 0 || natts > MaxHeapAttributeNumber)
elog(WARN, "fillatt: %d attributes is too large", natts);
if (natts == 0)
{
elog(DEBUG, "fillatt: called with natts == 0");
return;
}
attributeP = &att[0];
for (i = 0; i < natts;)
{
tuple = SearchSysCacheTuple(TYPOID,
Int32GetDatum((*attributeP)->atttypid),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
{
elog(WARN, "fillatt: unknown atttypid %ld",
(*attributeP)->atttypid);
}
else
{
(*attributeP)->attnum = (int16)++ i;
/*
* Check if the attr is a set before messing with the length
* and byval, since those were already set in
* TupleDescInitEntry. In fact, this seems redundant here,
* but who knows what I'll break if I take it out...
*
* same for char() and varchar() stuff. I share the same
* sentiments. This function is poorly written anyway. -ay
* 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--
* This file contains routines to support indices defined on system
* catalogs.
* This file contains routines to support indices defined on system
* catalogs.
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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:
*
* pg_attribute
* pg_proc
* pg_type
* pg_naming
* pg_class
* pg_attrdef
* pg_relcheck
* pg_trigger
* pg_attribute
* pg_proc
* pg_type
* pg_naming
* pg_class
* pg_attrdef
* pg_relcheck
* pg_trigger
*/
char *Name_pg_attr_indices[Num_pg_attr_indices] = {AttributeNameIndex,
AttributeNumIndex,
AttributeRelidIndex};
char *Name_pg_proc_indices[Num_pg_proc_indices] = { ProcedureNameIndex,
ProcedureOidIndex,
ProcedureSrcIndex};
char *Name_pg_type_indices[Num_pg_type_indices] = { TypeNameIndex,
TypeOidIndex};
char *Name_pg_class_indices[Num_pg_class_indices]= { ClassNameIndex,
ClassOidIndex};
char *Name_pg_attrdef_indices[Num_pg_attrdef_indices]= { AttrDefaultIndex };
char *Name_pg_attr_indices[Num_pg_attr_indices] = {AttributeNameIndex,
AttributeNumIndex,
AttributeRelidIndex};
char *Name_pg_proc_indices[Num_pg_proc_indices] = {ProcedureNameIndex,
ProcedureOidIndex,
ProcedureSrcIndex};
char *Name_pg_type_indices[Num_pg_type_indices] = {TypeNameIndex,
TypeOidIndex};
char *Name_pg_class_indices[Num_pg_class_indices] = {ClassNameIndex,
ClassOidIndex};
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,
Relation idesc,
ScanKey skey);
static HeapTuple
CatalogIndexFetchTuple(Relation heapRelation,
Relation idesc,
ScanKey skey);
/*
@ -75,11 +76,11 @@ static HeapTuple CatalogIndexFetchTuple(Relation heapRelation,
void
CatalogOpenIndices(int nIndices, char *names[], Relation idescs[])
{
int i;
for (i=0; i<nIndices; i++)
int i;
for (i = 0; i < nIndices; i++)
{
idescs[i] = index_openr(names[i]);
idescs[i] = index_openr(names[i]);
}
}
@ -87,83 +88,84 @@ CatalogOpenIndices(int nIndices, char *names[], Relation idescs[])
* This is the inverse routine to CatalogOpenIndices()
*/
void
CatalogCloseIndices(int nIndices, Relation *idescs)
CatalogCloseIndices(int nIndices, Relation * idescs)
{
int i;
for (i=0; i<nIndices; i++)
index_close(idescs[i]);
int i;
for (i = 0; i < nIndices; i++)
index_close(idescs[i]);
}
/*
* For the same reasons outlined above CatalogOpenIndices() we need a routine
* that takes a new catalog tuple and inserts an associated index tuple into
* that takes a new catalog tuple and inserts an associated index tuple into
* each catalog index.
*/
void
CatalogIndexInsert(Relation *idescs,
int nIndices,
Relation heapRelation,
HeapTuple heapTuple)
CatalogIndexInsert(Relation * idescs,
int nIndices,
Relation heapRelation,
HeapTuple heapTuple)
{
HeapTuple pgIndexTup;
TupleDesc heapDescriptor;
IndexTupleForm pgIndexP;
Datum datum;
int natts;
AttrNumber *attnumP;
FuncIndexInfo finfo, *finfoP;
char nulls[INDEX_MAX_KEYS];
int i;
heapDescriptor = RelationGetTupleDescriptor(heapRelation);
for (i=0; i<nIndices; i++)
HeapTuple pgIndexTup;
TupleDesc heapDescriptor;
IndexTupleForm pgIndexP;
Datum datum;
int natts;
AttrNumber *attnumP;
FuncIndexInfo finfo,
*finfoP;
char nulls[INDEX_MAX_KEYS];
int i;
heapDescriptor = RelationGetTupleDescriptor(heapRelation);
for (i = 0; i < nIndices; i++)
{
TupleDesc indexDescriptor;
InsertIndexResult indexRes;
indexDescriptor = RelationGetTupleDescriptor(idescs[i]);
pgIndexTup = SearchSysCacheTuple(INDEXRELID,
Int32GetDatum(idescs[i]->rd_id),
0,0,0);
Assert(pgIndexTup);
pgIndexP = (IndexTupleForm)GETSTRUCT(pgIndexTup);
/*
* Compute the number of attributes we are indexing upon.
* very important - can't assume one if this is a functional
* index.
*/
for (attnumP=(&pgIndexP->indkey[0]), natts=0;
*attnumP != InvalidAttrNumber;
attnumP++, natts++)
;
if (pgIndexP->indproc != InvalidOid)
TupleDesc indexDescriptor;
InsertIndexResult indexRes;
indexDescriptor = RelationGetTupleDescriptor(idescs[i]);
pgIndexTup = SearchSysCacheTuple(INDEXRELID,
Int32GetDatum(idescs[i]->rd_id),
0, 0, 0);
Assert(pgIndexTup);
pgIndexP = (IndexTupleForm) GETSTRUCT(pgIndexTup);
/*
* Compute the number of attributes we are indexing upon. very
* important - can't assume one if this is a functional index.
*/
for (attnumP = (&pgIndexP->indkey[0]), natts = 0;
*attnumP != InvalidAttrNumber;
attnumP++, natts++)
;
if (pgIndexP->indproc != InvalidOid)
{
FIgetnArgs(&finfo) = natts;
natts = 1;
FIgetProcOid(&finfo) = pgIndexP->indproc;
*(FIgetname(&finfo)) = '\0';
finfoP = &finfo;
FIgetnArgs(&finfo) = natts;
natts = 1;
FIgetProcOid(&finfo) = pgIndexP->indproc;
*(FIgetname(&finfo)) = '\0';
finfoP = &finfo;
}
else
finfoP = (FuncIndexInfo *)NULL;
FormIndexDatum(natts,
(AttrNumber *)&pgIndexP->indkey[0],
heapTuple,
heapDescriptor,
InvalidBuffer,
&datum,
nulls,
finfoP);
indexRes = index_insert(idescs[i], &datum, nulls,
&(heapTuple->t_ctid), heapRelation);
if (indexRes) pfree(indexRes);
else
finfoP = (FuncIndexInfo *) NULL;
FormIndexDatum(natts,
(AttrNumber *) & pgIndexP->indkey[0],
heapTuple,
heapDescriptor,
InvalidBuffer,
&datum,
nulls,
finfoP);
indexRes = index_insert(idescs[i], &datum, nulls,
&(heapTuple->t_ctid), heapRelation);
if (indexRes)
pfree(indexRes);
}
}
@ -174,81 +176,88 @@ CatalogIndexInsert(Relation *idescs,
bool
CatalogHasIndex(char *catName, Oid catId)
{
Relation pg_class;
HeapTuple htup;
Form_pg_class pgRelP;
int i;
Assert(IsSystemRelationName(catName));
/*
* If we're bootstraping we don't have pg_class (or any indices).
*/
if (IsBootstrapProcessingMode())
return false;
if (IsInitProcessingMode()) {
for (i = 0; IndexedCatalogNames[i] != NULL; i++) {
if ( strcmp(IndexedCatalogNames[i], catName) == 0)
return (true);
Relation pg_class;
HeapTuple htup;
Form_pg_class pgRelP;
int i;
Assert(IsSystemRelationName(catName));
/*
* If we're bootstraping we don't have pg_class (or any indices).
*/
if (IsBootstrapProcessingMode())
return false;
if (IsInitProcessingMode())
{
for (i = 0; IndexedCatalogNames[i] != NULL; i++)
{
if (strcmp(IndexedCatalogNames[i], catName) == 0)
return (true);
}
return (false);
}
return (false);
}
pg_class = heap_openr(RelationRelationName);
htup = ClassOidIndexScan(pg_class, catId);
heap_close(pg_class);
if (! HeapTupleIsValid(htup)) {
elog(NOTICE, "CatalogHasIndex: no relation with oid %d", catId);
return false;
}
pgRelP = (Form_pg_class)GETSTRUCT(htup);
return (pgRelP->relhasindex);
pg_class = heap_openr(RelationRelationName);
htup = ClassOidIndexScan(pg_class, catId);
heap_close(pg_class);
if (!HeapTupleIsValid(htup))
{
elog(NOTICE, "CatalogHasIndex: no relation with oid %d", catId);
return false;
}
pgRelP = (Form_pg_class) GETSTRUCT(htup);
return (pgRelP->relhasindex);
}
/*
* CatalogIndexFetchTuple() -- Get a tuple that satisfies a scan key
* from a catalog relation.
* CatalogIndexFetchTuple() -- Get a tuple that satisfies a scan key
* from a catalog relation.
*
* 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
* key.
* 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
* key.
*/
static HeapTuple
static HeapTuple
CatalogIndexFetchTuple(Relation heapRelation,
Relation idesc,
ScanKey skey)
Relation idesc,
ScanKey skey)
{
IndexScanDesc sd;
RetrieveIndexResult indexRes;
HeapTuple tuple;
Buffer buffer;
sd = index_beginscan(idesc, false, 1, skey);
tuple = (HeapTuple)NULL;
do {
indexRes = index_getnext(sd, ForwardScanDirection);
if (indexRes) {
ItemPointer iptr;
iptr = &indexRes->heap_iptr;
tuple = heap_fetch(heapRelation, NowTimeQual, iptr, &buffer);
pfree(indexRes);
} else
break;
} while (!HeapTupleIsValid(tuple));
if (HeapTupleIsValid(tuple)) {
tuple = heap_copytuple(tuple);
ReleaseBuffer(buffer);
}
index_endscan(sd);
pfree(sd);
return (tuple);
IndexScanDesc sd;
RetrieveIndexResult indexRes;
HeapTuple tuple;
Buffer buffer;
sd = index_beginscan(idesc, false, 1, skey);
tuple = (HeapTuple) NULL;
do
{
indexRes = index_getnext(sd, ForwardScanDirection);
if (indexRes)
{
ItemPointer iptr;
iptr = &indexRes->heap_iptr;
tuple = heap_fetch(heapRelation, NowTimeQual, iptr, &buffer);
pfree(indexRes);
}
else
break;
} while (!HeapTupleIsValid(tuple));
if (HeapTupleIsValid(tuple))
{
tuple = heap_copytuple(tuple);
ReleaseBuffer(buffer);
}
index_endscan(sd);
pfree(sd);
return (tuple);
}
/*
@ -259,276 +268,295 @@ CatalogIndexFetchTuple(Relation heapRelation,
*/
HeapTuple
AttributeNameIndexScan(Relation heapRelation,
Oid relid,
char *attname)
Oid relid,
char *attname)
{
Relation idesc;
ScanKeyData skey;
OidName keyarg;
HeapTuple tuple;
keyarg = mkoidname(relid, attname);
ScanKeyEntryInitialize(&skey,
(bits16)0x0,
(AttrNumber)1,
(RegProcedure)OidNameEqRegProcedure,
(Datum)keyarg);
idesc = index_openr(AttributeNameIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
index_close(idesc);
pfree(keyarg);
return tuple;
Relation idesc;
ScanKeyData skey;
OidName keyarg;
HeapTuple tuple;
keyarg = mkoidname(relid, attname);
ScanKeyEntryInitialize(&skey,
(bits16) 0x0,
(AttrNumber) 1,
(RegProcedure) OidNameEqRegProcedure,
(Datum) keyarg);
idesc = index_openr(AttributeNameIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
index_close(idesc);
pfree(keyarg);
return tuple;
}
HeapTuple
AttributeNumIndexScan(Relation heapRelation,
Oid relid,
AttrNumber attnum)
Oid relid,
AttrNumber attnum)
{
Relation idesc;
ScanKeyData skey;
OidInt2 keyarg;
HeapTuple tuple;
keyarg = mkoidint2(relid, (uint16)attnum);
ScanKeyEntryInitialize(&skey,
(bits16)0x0,
(AttrNumber)1,
(RegProcedure)OidInt2EqRegProcedure,
(Datum)keyarg);
idesc = index_openr(AttributeNumIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
index_close(idesc);
pfree(keyarg);
return tuple;
Relation idesc;
ScanKeyData skey;
OidInt2 keyarg;
HeapTuple tuple;
keyarg = mkoidint2(relid, (uint16) attnum);
ScanKeyEntryInitialize(&skey,
(bits16) 0x0,
(AttrNumber) 1,
(RegProcedure) OidInt2EqRegProcedure,
(Datum) keyarg);
idesc = index_openr(AttributeNumIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
index_close(idesc);
pfree(keyarg);
return tuple;
}
HeapTuple
ProcedureOidIndexScan(Relation heapRelation, Oid procId)
{
Relation idesc;
ScanKeyData skey;
HeapTuple tuple;
ScanKeyEntryInitialize(&skey,
(bits16)0x0,
(AttrNumber)1,
(RegProcedure)ObjectIdEqualRegProcedure,
(Datum)procId);
idesc = index_openr(ProcedureOidIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
index_close(idesc);
return tuple;
Relation idesc;
ScanKeyData skey;
HeapTuple tuple;
ScanKeyEntryInitialize(&skey,
(bits16) 0x0,
(AttrNumber) 1,
(RegProcedure) ObjectIdEqualRegProcedure,
(Datum) procId);
idesc = index_openr(ProcedureOidIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
index_close(idesc);
return tuple;
}
HeapTuple
ProcedureNameIndexScan(Relation heapRelation,
char *procName,
int nargs,
Oid *argTypes)
char *procName,
int nargs,
Oid * argTypes)
{
Relation idesc;
ScanKeyData skey;
HeapTuple tuple; /* tuple being tested */
HeapTuple return_tuple; /* The tuple pointer we eventually return */
IndexScanDesc sd;
RetrieveIndexResult indexRes;
Buffer buffer;
Form_pg_proc pgProcP;
bool ScanComplete;
/* The index scan is complete, i.e. we've scanned everything there
is to scan.
*/
bool FoundMatch;
/* In scanning pg_proc, we have found a row that meets our search
criteria.
*/
ScanKeyEntryInitialize(&skey,
(bits16)0x0,
(AttrNumber)1,
(RegProcedure)NameEqualRegProcedure,
(Datum)procName);
idesc = index_openr(ProcedureNameIndex);
sd = index_beginscan(idesc, false, 1, &skey);
/*
* 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;
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;
}
Relation idesc;
ScanKeyData skey;
HeapTuple tuple; /* tuple being tested */
HeapTuple return_tuple; /* The tuple pointer we eventually
* return */
IndexScanDesc sd;
RetrieveIndexResult indexRes;
Buffer buffer;
Form_pg_proc pgProcP;
bool ScanComplete;
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;
/*
* The index scan is complete, i.e. we've scanned everything there is
* to scan.
*/
bool FoundMatch;
/*
* In scanning pg_proc, we have found a row that meets our search
* criteria.
*/
ScanKeyEntryInitialize(&skey,
(bits16) 0x0,
(AttrNumber) 1,
(RegProcedure) NameEqualRegProcedure,
(Datum) procName);
idesc = index_openr(ProcedureNameIndex);
sd = index_beginscan(idesc, false, 1, &skey);
/*
* 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;
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));
return_tuple = heap_copytuple(tuple);
ReleaseBuffer(buffer);
}
else
return_tuple = (HeapTuple) NULL;
index_endscan(sd);
index_close(idesc);
return return_tuple;
}
HeapTuple
ProcedureSrcIndexScan(Relation heapRelation, text *procSrc)
ProcedureSrcIndexScan(Relation heapRelation, text * procSrc)
{
Relation idesc;
IndexScanDesc sd;
ScanKeyData skey;
RetrieveIndexResult indexRes;
HeapTuple tuple;
Buffer buffer;
ScanKeyEntryInitialize(&skey,
(bits16)0x0,
(AttrNumber)Anum_pg_proc_prosrc,
(RegProcedure)TextEqualRegProcedure,
(Datum)procSrc);
idesc = index_openr(ProcedureSrcIndex);
sd = index_beginscan(idesc, false, 1, &skey);
indexRes = index_getnext(sd, ForwardScanDirection);
if (indexRes) {
ItemPointer iptr;
iptr = &indexRes->heap_iptr;
tuple = heap_fetch(heapRelation, NowTimeQual, iptr, &buffer);
pfree(indexRes);
} else
tuple = (HeapTuple)NULL;
if (HeapTupleIsValid(tuple)) {
tuple = heap_copytuple(tuple);
ReleaseBuffer(buffer);
}
index_endscan(sd);
return tuple;
Relation idesc;
IndexScanDesc sd;
ScanKeyData skey;
RetrieveIndexResult indexRes;
HeapTuple tuple;
Buffer buffer;
ScanKeyEntryInitialize(&skey,
(bits16) 0x0,
(AttrNumber) Anum_pg_proc_prosrc,
(RegProcedure) TextEqualRegProcedure,
(Datum) procSrc);
idesc = index_openr(ProcedureSrcIndex);
sd = index_beginscan(idesc, false, 1, &skey);
indexRes = index_getnext(sd, ForwardScanDirection);
if (indexRes)
{
ItemPointer iptr;
iptr = &indexRes->heap_iptr;
tuple = heap_fetch(heapRelation, NowTimeQual, iptr, &buffer);
pfree(indexRes);
}
else
tuple = (HeapTuple) NULL;
if (HeapTupleIsValid(tuple))
{
tuple = heap_copytuple(tuple);
ReleaseBuffer(buffer);
}
index_endscan(sd);
return tuple;
}
HeapTuple
TypeOidIndexScan(Relation heapRelation, Oid typeId)
{
Relation idesc;
ScanKeyData skey;
HeapTuple tuple;
ScanKeyEntryInitialize(&skey,
(bits16)0x0,
(AttrNumber)1,
(RegProcedure)ObjectIdEqualRegProcedure,
(Datum)typeId);
idesc = index_openr(TypeOidIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
index_close(idesc);
return tuple;
Relation idesc;
ScanKeyData skey;
HeapTuple tuple;
ScanKeyEntryInitialize(&skey,
(bits16) 0x0,
(AttrNumber) 1,
(RegProcedure) ObjectIdEqualRegProcedure,
(Datum) typeId);
idesc = index_openr(TypeOidIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
index_close(idesc);
return tuple;
}
HeapTuple
TypeNameIndexScan(Relation heapRelation, char *typeName)
{
Relation idesc;
ScanKeyData skey;
HeapTuple tuple;
ScanKeyEntryInitialize(&skey,
(bits16)0x0,
(AttrNumber)1,
(RegProcedure)NameEqualRegProcedure,
(Datum)typeName);
idesc = index_openr(TypeNameIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
index_close(idesc);
return tuple;
Relation idesc;
ScanKeyData skey;
HeapTuple tuple;
ScanKeyEntryInitialize(&skey,
(bits16) 0x0,
(AttrNumber) 1,
(RegProcedure) NameEqualRegProcedure,
(Datum) typeName);
idesc = index_openr(TypeNameIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
index_close(idesc);
return tuple;
}
HeapTuple
ClassNameIndexScan(Relation heapRelation, char *relName)
{
Relation idesc;
ScanKeyData skey;
HeapTuple tuple;
ScanKeyEntryInitialize(&skey,
(bits16)0x0,
(AttrNumber)1,
(RegProcedure)NameEqualRegProcedure,
(Datum)relName);
idesc = index_openr(ClassNameIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
index_close(idesc);
return tuple;
Relation idesc;
ScanKeyData skey;
HeapTuple tuple;
ScanKeyEntryInitialize(&skey,
(bits16) 0x0,
(AttrNumber) 1,
(RegProcedure) NameEqualRegProcedure,
(Datum) relName);
idesc = index_openr(ClassNameIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
index_close(idesc);
return tuple;
}
HeapTuple
ClassOidIndexScan(Relation heapRelation, Oid relId)
{
Relation idesc;
ScanKeyData skey;
HeapTuple tuple;
ScanKeyEntryInitialize(&skey,
(bits16)0x0,
(AttrNumber)1,
(RegProcedure)ObjectIdEqualRegProcedure,
(Datum)relId);
idesc = index_openr(ClassOidIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
index_close(idesc);
return tuple;
Relation idesc;
ScanKeyData skey;
HeapTuple tuple;
ScanKeyEntryInitialize(&skey,
(bits16) 0x0,
(AttrNumber) 1,
(RegProcedure) ObjectIdEqualRegProcedure,
(Datum) relId);
idesc = index_openr(ClassOidIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
index_close(idesc);
return tuple;
}

View File

@ -1,13 +1,13 @@
/*-------------------------------------------------------------------------
*
* 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
*
*
* 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 <miscadmin.h>
#ifndef HAVE_MEMMOVE
# include <regex/utils.h>
#include <regex/utils.h>
#else
# include <string.h>
#include <string.h>
#endif
/* ----------------
@ -34,288 +34,303 @@
*
* aggregates overloading has been added. Instead of the full
* overload support we have for functions, aggregate overloading only
* applies to exact basetype matches. That is, we don't check the
* applies to exact basetype matches. That is, we don't check the
* the inheritance hierarchy
*
* OLD COMMENTS:
* Currently, redefining aggregates using the same name is not
* supported. In such a case, a warning is printed that the
* aggregate already exists. If such is not the case, a new tuple
* is created and inserted in the aggregate relation. The fields
* of this tuple are aggregate name, owner id, 2 transition functions
* (called aggtransfn1 and aggtransfn2), final function (aggfinalfn),
* type of data on which aggtransfn1 operates (aggbasetype), return
* types of the two transition functions (aggtranstype1 and
* aggtranstype2), final return type (aggfinaltype), and initial values
* for the two state transition functions (agginitval1 and agginitval2).
* All types and functions must have been defined
* prior to defining the aggregate.
*
* Currently, redefining aggregates using the same name is not
* supported. In such a case, a warning is printed that the
* aggregate already exists. If such is not the case, a new tuple
* is created and inserted in the aggregate relation. The fields
* of this tuple are aggregate name, owner id, 2 transition functions
* (called aggtransfn1 and aggtransfn2), final function (aggfinalfn),
* type of data on which aggtransfn1 operates (aggbasetype), return
* types of the two transition functions (aggtranstype1 and
* aggtranstype2), final return type (aggfinaltype), and initial values
* for the two state transition functions (agginitval1 and agginitval2).
* All types and functions must have been defined
* prior to defining the aggregate.
*
* ---------------
*/
void
AggregateCreate(char *aggName,
char *aggtransfn1Name,
char *aggtransfn2Name,
char *aggfinalfnName,
char *aggbasetypeName,
char *aggtransfn1typeName,
char *aggtransfn2typeName,
char *agginitval1,
char *agginitval2)
char *aggtransfn1Name,
char *aggtransfn2Name,
char *aggfinalfnName,
char *aggbasetypeName,
char *aggtransfn1typeName,
char *aggtransfn2typeName,
char *agginitval1,
char *agginitval2)
{
register i;
Relation aggdesc;
HeapTuple tup;
char nulls[Natts_pg_aggregate];
Datum values[Natts_pg_aggregate];
Form_pg_proc proc;
Oid xfn1 = InvalidOid;
Oid xfn2 = InvalidOid;
Oid ffn = InvalidOid;
Oid xbase = InvalidOid;
Oid xret1 = InvalidOid;
Oid xret2 = InvalidOid;
Oid fret = InvalidOid;
Oid fnArgs[8];
TupleDesc tupDesc;
memset(fnArgs, 0, 8 * sizeof(Oid));
/* sanity checks */
if (!aggName)
elog(WARN, "AggregateCreate: no aggregate name supplied");
if (!aggtransfn1Name && !aggtransfn2Name)
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;
register i;
Relation aggdesc;
HeapTuple tup;
char nulls[Natts_pg_aggregate];
Datum values[Natts_pg_aggregate];
Form_pg_proc proc;
Oid xfn1 = InvalidOid;
Oid xfn2 = InvalidOid;
Oid ffn = InvalidOid;
Oid xbase = InvalidOid;
Oid xret1 = InvalidOid;
Oid xret2 = InvalidOid;
Oid fret = InvalidOid;
Oid fnArgs[8];
TupleDesc tupDesc;
if (aggtransfn1Name) {
tup = SearchSysCacheTuple(TYPNAME,
PointerGetDatum(aggtransfn1typeName),
0,0,0);
if(!HeapTupleIsValid(tup))
elog(WARN, "AggregateCreate: Type '%s' undefined",
aggtransfn1typeName);
xret1 = tup->t_oid;
fnArgs[0] = xret1;
fnArgs[1] = xbase;
tup = SearchSysCacheTuple(PRONAME,
PointerGetDatum(aggtransfn1Name),
Int32GetDatum(2),
PointerGetDatum(fnArgs),
0);
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);
}
if (aggtransfn2Name) {
tup = SearchSysCacheTuple(TYPNAME,
PointerGetDatum(aggtransfn2typeName),
0,0,0);
if(!HeapTupleIsValid(tup))
elog(WARN, "AggregateCreate: Type '%s' undefined",
aggtransfn2typeName);
xret2 = tup->t_oid;
fnArgs[0] = xret2;
fnArgs[1] = 0;
tup = SearchSysCacheTuple(PRONAME,
PointerGetDatum(aggtransfn2Name),
Int32GetDatum(1),
PointerGetDatum(fnArgs),
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);
}
tup = SearchSysCacheTuple(AGGNAME, PointerGetDatum(aggName),
ObjectIdGetDatum(xbase),
0,0);
if (HeapTupleIsValid(tup))
elog(WARN,
"AggregateCreate: aggregate '%s' with base type '%s' already exists",
aggName, aggbasetypeName);
memset(fnArgs, 0, 8 * sizeof(Oid));
/* more sanity checks */
if (aggtransfn1Name && aggtransfn2Name && !aggfinalfnName)
elog(WARN, "AggregateCreate: Aggregate must have final function with both transition functions");
if ((!aggtransfn1Name || !aggtransfn2Name) && aggfinalfnName)
elog(WARN, "AggregateCreate: Aggregate cannot have final function without both transition functions");
if (aggfinalfnName) {
fnArgs[0] = xret1;
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 transition function 2 is defined, it must have an initial value,
* whereas transition function 1 does not, which allows man and min
* aggregates to return NULL if they are evaluated on empty sets.
*/
if (OidIsValid(xfn2) && !agginitval2)
elog(WARN, "AggregateCreate: transition function 2 MUST have an initial value");
/* initialize nulls and values */
for(i=0; i < Natts_pg_aggregate; i++) {
nulls[i] = ' ';
values[i] = (Datum)NULL;
}
values[Anum_pg_aggregate_aggname-1] = PointerGetDatum(aggName);
values[Anum_pg_aggregate_aggowner-1] =
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] =
ObjectIdGetDatum(xbase);
if (!OidIsValid(xfn1)) {
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 (agginitval1)
values[Anum_pg_aggregate_agginitval1-1] = PointerGetDatum(textin(agginitval1));
else
nulls[Anum_pg_aggregate_agginitval1-1] = 'n';
if (agginitval2)
values[Anum_pg_aggregate_agginitval2-1] = PointerGetDatum(textin(agginitval2));
else
nulls[Anum_pg_aggregate_agginitval2-1] = 'n';
if (!RelationIsValid(aggdesc = heap_openr(AggregateRelationName)))
elog(WARN, "AggregateCreate: could not open '%s'",
AggregateRelationName);
/* sanity checks */
if (!aggName)
elog(WARN, "AggregateCreate: no aggregate name supplied");
tupDesc = aggdesc->rd_att;
if (!HeapTupleIsValid(tup = heap_formtuple(tupDesc,
values,
nulls)))
elog(WARN, "AggregateCreate: heap_formtuple failed");
if (!OidIsValid(heap_insert(aggdesc, tup)))
elog(WARN, "AggregateCreate: heap_insert failed");
heap_close(aggdesc);
if (!aggtransfn1Name && !aggtransfn2Name)
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,
PointerGetDatum(aggtransfn1typeName),
0, 0, 0);
if (!HeapTupleIsValid(tup))
elog(WARN, "AggregateCreate: Type '%s' undefined",
aggtransfn1typeName);
xret1 = tup->t_oid;
fnArgs[0] = xret1;
fnArgs[1] = xbase;
tup = SearchSysCacheTuple(PRONAME,
PointerGetDatum(aggtransfn1Name),
Int32GetDatum(2),
PointerGetDatum(fnArgs),
0);
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);
}
if (aggtransfn2Name)
{
tup = SearchSysCacheTuple(TYPNAME,
PointerGetDatum(aggtransfn2typeName),
0, 0, 0);
if (!HeapTupleIsValid(tup))
elog(WARN, "AggregateCreate: Type '%s' undefined",
aggtransfn2typeName);
xret2 = tup->t_oid;
fnArgs[0] = xret2;
fnArgs[1] = 0;
tup = SearchSysCacheTuple(PRONAME,
PointerGetDatum(aggtransfn2Name),
Int32GetDatum(1),
PointerGetDatum(fnArgs),
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);
}
tup = SearchSysCacheTuple(AGGNAME, PointerGetDatum(aggName),
ObjectIdGetDatum(xbase),
0, 0);
if (HeapTupleIsValid(tup))
elog(WARN,
"AggregateCreate: aggregate '%s' with base type '%s' already exists",
aggName, aggbasetypeName);
/* more sanity checks */
if (aggtransfn1Name && aggtransfn2Name && !aggfinalfnName)
elog(WARN, "AggregateCreate: Aggregate must have final function with both transition functions");
if ((!aggtransfn1Name || !aggtransfn2Name) && aggfinalfnName)
elog(WARN, "AggregateCreate: Aggregate cannot have final function without both transition functions");
if (aggfinalfnName)
{
fnArgs[0] = xret1;
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 transition function 2 is defined, it must have an initial value,
* whereas transition function 1 does not, which allows man and min
* aggregates to return NULL if they are evaluated on empty sets.
*/
if (OidIsValid(xfn2) && !agginitval2)
elog(WARN, "AggregateCreate: transition function 2 MUST have an initial value");
/* initialize nulls and values */
for (i = 0; i < Natts_pg_aggregate; i++)
{
nulls[i] = ' ';
values[i] = (Datum) NULL;
}
values[Anum_pg_aggregate_aggname - 1] = PointerGetDatum(aggName);
values[Anum_pg_aggregate_aggowner - 1] =
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] =
ObjectIdGetDatum(xbase);
if (!OidIsValid(xfn1))
{
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 (agginitval1)
values[Anum_pg_aggregate_agginitval1 - 1] = PointerGetDatum(textin(agginitval1));
else
nulls[Anum_pg_aggregate_agginitval1 - 1] = 'n';
if (agginitval2)
values[Anum_pg_aggregate_agginitval2 - 1] = PointerGetDatum(textin(agginitval2));
else
nulls[Anum_pg_aggregate_agginitval2 - 1] = 'n';
if (!RelationIsValid(aggdesc = heap_openr(AggregateRelationName)))
elog(WARN, "AggregateCreate: could not open '%s'",
AggregateRelationName);
tupDesc = aggdesc->rd_att;
if (!HeapTupleIsValid(tup = heap_formtuple(tupDesc,
values,
nulls)))
elog(WARN, "AggregateCreate: heap_formtuple failed");
if (!OidIsValid(heap_insert(aggdesc, tup)))
elog(WARN, "AggregateCreate: heap_insert failed");
heap_close(aggdesc);
}
char *
AggNameGetInitVal(char *aggName, Oid basetype, int xfuncno, bool *isNull)
char *
AggNameGetInitVal(char *aggName, Oid basetype, int xfuncno, bool * isNull)
{
HeapTuple tup;
Relation aggRel;
int initValAttno;
Oid transtype;
text *textInitVal;
char *strInitVal, *initVal;
Assert(PointerIsValid(aggName));
Assert(PointerIsValid(isNull));
Assert(xfuncno == 1 || xfuncno == 2);
HeapTuple tup;
Relation aggRel;
int initValAttno;
Oid transtype;
text *textInitVal;
char *strInitVal,
*initVal;
tup = SearchSysCacheTuple(AGGNAME,
PointerGetDatum(aggName),
PointerGetDatum(basetype),
0,0);
if (!HeapTupleIsValid(tup))
elog(WARN, "AggNameGetInitVal: cache lookup failed for aggregate '%s'",
aggName);
if (xfuncno == 1) {
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;
initValAttno = Anum_pg_aggregate_agginitval2;
}
aggRel = heap_openr(AggregateRelationName);
if (!RelationIsValid(aggRel))
elog(WARN, "AggNameGetInitVal: could not open \"%-.*s\"",
AggregateRelationName);
/*
* must use fastgetattr in case one or other of the init values is NULL
*/
textInitVal = (text *) fastgetattr(tup, initValAttno,
RelationGetTupleDescriptor(aggRel),
isNull);
if (!PointerIsValid(textInitVal))
*isNull = true;
if (*isNull) {
Assert(PointerIsValid(aggName));
Assert(PointerIsValid(isNull));
Assert(xfuncno == 1 || xfuncno == 2);
tup = SearchSysCacheTuple(AGGNAME,
PointerGetDatum(aggName),
PointerGetDatum(basetype),
0, 0);
if (!HeapTupleIsValid(tup))
elog(WARN, "AggNameGetInitVal: cache lookup failed for aggregate '%s'",
aggName);
if (xfuncno == 1)
{
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;
initValAttno = Anum_pg_aggregate_agginitval2;
}
aggRel = heap_openr(AggregateRelationName);
if (!RelationIsValid(aggRel))
elog(WARN, "AggNameGetInitVal: could not open \"%-.*s\"",
AggregateRelationName);
/*
* must use fastgetattr in case one or other of the init values is
* NULL
*/
textInitVal = (text *) fastgetattr(tup, initValAttno,
RelationGetTupleDescriptor(aggRel),
isNull);
if (!PointerIsValid(textInitVal))
*isNull = true;
if (*isNull)
{
heap_close(aggRel);
return ((char *) NULL);
}
strInitVal = textout(textInitVal);
heap_close(aggRel);
return((char *) NULL);
}
strInitVal = textout(textInitVal);
heap_close(aggRel);
tup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(transtype),
0,0,0);
if (!HeapTupleIsValid(tup)) {
tup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(transtype),
0, 0, 0);
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);
elog(WARN, "AggNameGetInitVal: cache lookup failed on aggregate transition function return type");
}
initVal = fmgr(((TypeTupleForm) GETSTRUCT(tup))->typinput, strInitVal, -1);
pfree(strInitVal);
return(initVal);
return (initVal);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,13 @@
/*-------------------------------------------------------------------------
*
* 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
*
*
* 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 <miscadmin.h>
#ifndef HAVE_MEMMOVE
# include <regex/utils.h>
#include <regex/utils.h>
#else
# include <string.h>
#include <string.h>
#endif
/* ----------------------------------------------------------------
* ProcedureDefine
* ProcedureDefine
* ----------------------------------------------------------------
*/
Oid
ProcedureCreate(char *procedureName,
bool returnsSet,
char *returnTypeName,
char *languageName,
char *prosrc,
char *probin,
bool canCache,
bool trusted,
int32 byte_pct,
int32 perbyte_cpu,
int32 percall_cpu,
int32 outin_ratio,
List *argList,
CommandDest dest)
bool returnsSet,
char *returnTypeName,
char *languageName,
char *prosrc,
char *probin,
bool canCache,
bool trusted,
int32 byte_pct,
int32 perbyte_cpu,
int32 percall_cpu,
int32 outin_ratio,
List * argList,
CommandDest dest)
{
register i;
Relation rdesc;
HeapTuple tup;
bool defined;
uint16 parameterCount;
char nulls[ Natts_pg_proc ];
Datum values[ Natts_pg_proc ];
Oid languageObjectId;
Oid typeObjectId;
List *x;
QueryTreeList *querytree_list;
List *plan_list;
Oid typev[8];
Oid relid;
Oid toid;
text *prosrctext;
TupleDesc tupDesc;
/* ----------------
* 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)) {
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;
}
register i;
Relation rdesc;
HeapTuple tup;
bool defined;
uint16 parameterCount;
char nulls[Natts_pg_proc];
Datum values[Natts_pg_proc];
Oid languageObjectId;
Oid typeObjectId;
List *x;
QueryTreeList *querytree_list;
List *plan_list;
Oid typev[8];
Oid relid;
Oid toid;
text *prosrctext;
TupleDesc tupDesc;
/* ----------------
* 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))
{
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--
* 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
*
* The version stuff has not been tested under postgres95 and probably doesn't
* work! - jolly 8/19/95
*
* The version stuff has not been tested under postgres95 and probably doesn't
* work! - jolly 8/19/95
*
*
* 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
* At the point the version is defined, 2 physical relations are created
* <vname>_added and <vname>_deleted.
* At the point the version is defined, 2 physical relations are created
* <vname>_added and <vname>_deleted.
*
* In addition, 4 rules are defined which govern the semantics of versions
* w.r.t retrieves, appends, replaces and deletes.
* In addition, 4 rules are defined which govern the semantics of versions
* w.r.t retrieves, appends, replaces and deletes.
*
*-------------------------------------------------------------------------
*/
@ -34,29 +34,31 @@
#define MAX_QUERY_LEN 1024
char rule_buf[MAX_QUERY_LEN];
char rule_buf[MAX_QUERY_LEN];
#ifdef NOT_USED
static char attr_list[MAX_QUERY_LEN];
static char attr_list[MAX_QUERY_LEN];
#endif
/*
* problem: the version system assumes that the rules it declares will
* be fired in the order of declaration, it also assumes
* goh's silly instead semantics. Unfortunately, it is a pain
* to make the version system work with the new semantics.
* However the whole problem can be solved, and some nice
* functionality can be achieved if we get multiple action rules
* to work. So thats what I did -- glass
* be fired in the order of declaration, it also assumes
* goh's silly instead semantics. Unfortunately, it is a pain
* to make the version system work with the new semantics.
* However the whole problem can be solved, and some nice
* functionality can be achieved if we get multiple action rules
* to work. So thats what I did -- glass
*
* Well, at least they've been working for about 20 minutes.
*
*
* So any comments in this code about 1 rule per transction are false...:)
*
*/
/*
* This is needed because the rule system only allows
* *1* rule to be defined per transaction.
* This is needed because the rule system only allows
* *1* rule to be defined per transaction.
*
* NOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
* OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
@ -80,267 +82,282 @@ static char attr_list[MAX_QUERY_LEN];
* a strange memory bug instead of watching the "Get Smart" marathon
* in NICK !
* DO NOT COMMIT THE XACT, just increase the Cid counter!
* _sp.
* _sp.
*/
#ifdef NOT_USED
static void
eval_as_new_xact(char *query)
{
/* WARNING! do not uncomment the following lines WARNING!
* CommitTransactionCommand();
* StartTransactionCommand();
*/
CommandCounterIncrement();
pg_eval(query, (char **) NULL, (Oid *) NULL, 0);
/*
* WARNING! do not uncomment the following lines WARNING!
* CommitTransactionCommand(); StartTransactionCommand();
*/
CommandCounterIncrement();
pg_eval(query, (char **) NULL, (Oid *) NULL, 0);
}
#endif
/*
* Define a version.
* Define a version.
*/
#ifdef NOT_USED
void
DefineVersion(char *name, char *fromRelname, char *date)
{
char *bname;
static char saved_basename[512];
static char saved_snapshot[512];
char *bname;
static char saved_basename[512];
static char saved_snapshot[512];
if (date == NULL) {
/* no time ranges */
bname = fromRelname;
strcpy(saved_basename, (char *) bname);
*saved_snapshot = (char)NULL;
} else {
/* 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
* from the base relation.
* Code is put here so that we only need to look up the attribute once for
* both appends and replaces.
*/
setAttrList(bname);
if (date == NULL)
{
/* no time ranges */
bname = fromRelname;
strcpy(saved_basename, (char *) bname);
*saved_snapshot = (char) NULL;
}
else
{
/* version is a snapshot */
bname = fromRelname;
strcpy(saved_basename, (char *) bname);
sprintf(saved_snapshot, "['%s']", date);
}
VersionCreate (name, saved_basename);
VersionAppend (name, saved_basename);
VersionDelete (name, saved_basename,saved_snapshot);
VersionReplace (name, saved_basename,saved_snapshot);
VersionRetrieve (name, saved_basename, saved_snapshot);
/*
* Calls the routine ``GetAttrList'' get the list of attributes from
* the base relation. Code is put here so that we only need to look up
* the attribute once for both appends and replaces.
*/
setAttrList(bname);
VersionCreate(name, saved_basename);
VersionAppend(name, saved_basename);
VersionDelete(name, saved_basename, saved_snapshot);
VersionReplace(name, saved_basename, saved_snapshot);
VersionRetrieve(name, saved_basename, saved_snapshot);
}
#endif
/*
* Creates the deltas.
* Creates the deltas.
*/
#ifdef NOT_USED
void
VersionCreate(char *vname, char *bname)
{
static char query_buf [MAX_QUERY_LEN];
/*
* Creating the dummy version relation for triggering rules.
*/
sprintf(query_buf, "SELECT * INTO TABLE %s from %s where 1 =2",
vname, bname);
pg_eval (query_buf, (char **) NULL, (Oid *) NULL, 0);
/*
* Creating the ``v_added'' relation
*/
sprintf (query_buf, "SELECT * INTO TABLE %s_added from %s where 1 = 2",
vname, bname);
eval_as_new_xact (query_buf);
/*
* Creating the ``v_deleted'' relation.
*/
sprintf (query_buf, "CREATE TABLE %s_del (DOID oid)", vname);
eval_as_new_xact (query_buf);
static char query_buf[MAX_QUERY_LEN];
/*
* Creating the dummy version relation for triggering rules.
*/
sprintf(query_buf, "SELECT * INTO TABLE %s from %s where 1 =2",
vname, bname);
pg_eval(query_buf, (char **) NULL, (Oid *) NULL, 0);
/*
* Creating the ``v_added'' relation
*/
sprintf(query_buf, "SELECT * INTO TABLE %s_added from %s where 1 = 2",
vname, bname);
eval_as_new_xact(query_buf);
/*
* Creating the ``v_deleted'' relation.
*/
sprintf(query_buf, "CREATE TABLE %s_del (DOID oid)", vname);
eval_as_new_xact(query_buf);
}
#endif
/*
* Given the relation name, does a catalog lookup for that relation and
* sets the global variable 'attr_list' with the list of attributes (names)
* for that relation.
* for that relation.
*/
#ifdef NOT_USED
static void
setAttrList(char *bname)
{
Relation rdesc;
int i = 0;
int maxattrs = 0;
char *attrname;
char temp_buf[512];
int notfirst = 0;
Relation rdesc;
int i = 0;
int maxattrs = 0;
char *attrname;
char temp_buf[512];
int notfirst = 0;
rdesc = heap_openr(bname);
if (rdesc == NULL ) {
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;
rdesc = heap_openr(bname);
if (rdesc == NULL)
{
elog(WARN, "Unable to expand all -- amopenr failed ");
return;
}
strcat(attr_list, temp_buf);
}
heap_close(rdesc);
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);
}
heap_close(rdesc);
return;
}
#endif
/*
* This routine defines the rule governing the append semantics of
* versions. All tuples appended to a version gets appended to the
* versions. All tuples appended to a version gets appended to the
* <vname>_added relation.
*/
#ifdef NOT_USED
static void
VersionAppend(char *vname, char *bname)
{
sprintf(rule_buf,
"define rewrite rule %s_append is on INSERT to %s do instead append %s_added(%s)",
vname, vname, vname, attr_list);
eval_as_new_xact(rule_buf);
sprintf(rule_buf,
"define rewrite rule %s_append is on INSERT to %s do instead append %s_added(%s)",
vname, vname, vname, attr_list);
eval_as_new_xact(rule_buf);
}
#endif
/*
* This routine defines the rule governing the retrieval semantics of
* versions. To retrieve tuples from a version , we need to:
*
* 1. Retrieve all tuples in the <vname>_added relation.
* 2. Retrieve all tuples in the base relation which are not in
* the <vname>_del relation.
* 1. Retrieve all tuples in the <vname>_added relation.
* 2. Retrieve all tuples in the base relation which are not in
* the <vname>_del relation.
*/
#ifdef NOT_USED
void
VersionRetrieve(char *vname, char *bname, char *snapshot)
{
sprintf(rule_buf,
"define rewrite rule %s_retrieve is on SELECT to %s do instead\n\
sprintf(rule_buf,
"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) \
where _%s.oid !!= '%s_del.DOID'",
vname, vname, vname, vname, bname,
bname, snapshot,
vname, vname, bname, bname, vname);
eval_as_new_xact(rule_buf);
/* printf("%s\n",rule_buf); */
vname, vname, vname, vname, bname,
bname, snapshot,
vname, vname, bname, bname, vname);
eval_as_new_xact(rule_buf);
/* printf("%s\n",rule_buf); */
}
#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:
*
* 1. If the tuple to be deleted was added to the version *after*
* the version was created, then we simply delete the tuple
* from the <vname>_added 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
* it to the <vname>_del relation.
* 1. If the tuple to be deleted was added to the version *after*
* the version was created, then we simply delete the tuple
* from the <vname>_added 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
* it to the <vname>_del relation.
*/
#ifdef NOT_USED
void
VersionDelete(char *vname, char *bname, char *snapshot)
{
sprintf(rule_buf,
"define rewrite rule %s_delete1 is on delete to %s do instead\n \
sprintf(rule_buf,
"define rewrite rule %s_delete1 is on delete to %s do instead\n \
[delete %s_added where current.oid = %s_added.oid\n \
append %s_del(DOID = current.oid) from _%s in %s%s \
where current.oid = _%s.oid] \n",
vname,vname,vname,vname,vname,
bname,bname,snapshot,bname);
vname, vname, vname, vname, vname,
bname, bname, snapshot, bname);
eval_as_new_xact(rule_buf);
eval_as_new_xact(rule_buf);
#ifdef OLD_REWRITE
sprintf(rule_buf,
"define rewrite rule %s_delete2 is on delete to %s do instead \n \
sprintf(rule_buf,
"define rewrite rule %s_delete2 is on delete to %s do instead \n \
append %s_del(DOID = current.oid) from _%s in %s%s \
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);
#endif /* OLD_REWRITE */
eval_as_new_xact(rule_buf);
#endif /* OLD_REWRITE */
}
#endif
/*
* This routine defines the rules that govern the update semantics
* of versions. To update a tuple in a version:
* This routine defines the rules that govern the update semantics
* of versions. To update a tuple in a version:
*
* 1. If the tuple is in <vname>_added, we simply ``replace''
* the tuple (as per postgres style).
* 2. if the tuple is in the base relation, then two things have to
* happen:
* 2.1 The tuple is marked ``deleted'' from the base relation by
* adding the tuple to the <vname>_del relation.
* 2.2 A copy of the tuple is appended to the <vname>_added relation
* 1. If the tuple is in <vname>_added, we simply ``replace''
* the tuple (as per postgres style).
* 2. if the tuple is in the base relation, then two things have to
* happen:
* 2.1 The tuple is marked ``deleted'' from the base relation by
* adding the tuple to the <vname>_del relation.
* 2.2 A copy of the tuple is appended to the <vname>_added relation
*/
#ifdef NOT_USED
void
VersionReplace(char *vname, char *bname, char *snapshot)
{
sprintf(rule_buf,
"define rewrite rule %s_replace1 is on replace to %s do instead \n\
sprintf(rule_buf,
"define rewrite rule %s_replace1 is on replace to %s do instead \n\
[replace %s_added(%s) where current.oid = %s_added.oid \n\
append %s_del(DOID = current.oid) from _%s in %s%s \
where current.oid = _%s.oid\n\
append %s_added(%s) from _%s in %s%s \
where current.oid !!= '%s_added.oid' and current.oid = _%s.oid]\n",
vname,vname,vname,attr_list,vname,
vname,bname,bname,snapshot,bname,
vname,attr_list,bname,bname,snapshot,vname,bname);
vname, vname, vname, attr_list, vname,
vname, bname, bname, snapshot, 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
sprintf(rule_buf,
"define rewrite rule %s_replace2 is on replace to %s do \n\
sprintf(rule_buf,
"define rewrite rule %s_replace2 is on replace to %s do \n\
append %s_del(DOID = current.oid) from _%s in %s%s \
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,
"define rewrite rule %s_replace3 is on replace to %s do instead\n\
sprintf(rule_buf,
"define rewrite rule %s_replace3 is on replace to %s do instead\n\
append %s_added(%s) from _%s in %s%s \
where current.oid !!= '%s_added.oid' and current.oid = \
_%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);
#endif /* OLD_REWRITE */
/* printf("%s\n",rule_buf); */
eval_as_new_xact(rule_buf);
#endif /* OLD_REWRITE */
/* printf("%s\n",rule_buf); */
}

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +1,20 @@
/*-------------------------------------------------------------------------
*
* 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
* 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
* be BUCKETS of comments. This is to allow reviewers to understand
* my (probably bogus) assumptions about the way this works.
* [pbrown '94]
* 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
* file. As I go - in contrast to the rest of postgres - there will
* be BUCKETS of comments. This is to allow reviewers to understand
* my (probably bogus) assumptions about the way this works.
* [pbrown '94]
*
* Copyright (c) 1994-5, Regents of the University of California
*
*
* 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>
#ifndef NO_SECURITY
#include <utils/acl.h>
#endif /* !NO_SECURITY */
#endif /* !NO_SECURITY */
static Relation copy_heap(Oid OIDOldHeap);
static void copy_index(Oid OIDOldIndex, Oid OIDNewHeap);
static void rebuildheap(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex);
static void copy_index(Oid OIDOldIndex, Oid OIDNewHeap);
static void rebuildheap(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex);
/*
* cluster
*
* Check that the relation is a relation in the appropriate user
* ACL. I will use the same security that limits users on the
* renamerel() function.
* Check that the relation is a relation in the appropriate user
* ACL. I will use the same security that limits users on the
* renamerel() function.
*
* Check that the index specified is appropriate for the task
* ( ie it's an index over this relation ). This is trickier.
* Check that the index specified is appropriate for the task
* ( ie it's an index over this relation ). This is trickier.
*
* 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
* 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
* 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
* hand, re-creating n indicies may blow out the space.
* 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
* 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
* 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
* hand, re-creating n indicies may blow out the space.
*
* Create new (temporary) relations for the base heap and the new
* index.
*
* Exclusively lock the relations.
*
* Create new clustered index and base heap relation.
* Create new (temporary) relations for the base heap and the new
* index.
*
* Exclusively lock the relations.
*
* Create new clustered index and base heap relation.
*
*/
void
cluster(char oldrelname[], char oldindexname[])
{
Oid OIDOldHeap, OIDOldIndex, OIDNewHeap;
Relation OldHeap, OldIndex;
Relation NewHeap;
char NewIndexName[NAMEDATALEN];
char NewHeapName[NAMEDATALEN];
char saveoldrelname[NAMEDATALEN];
char saveoldindexname[NAMEDATALEN];
Oid OIDOldHeap,
OIDOldIndex,
OIDNewHeap;
Relation OldHeap,
OldIndex;
Relation NewHeap;
char NewIndexName[NAMEDATALEN];
char NewHeapName[NAMEDATALEN];
char saveoldrelname[NAMEDATALEN];
char saveoldindexname[NAMEDATALEN];
/* Save the old names because they will get lost when the old relations
* are destroyed.
*/
strcpy(saveoldrelname, oldrelname);
strcpy(saveoldindexname, oldindexname);
/*
*
* 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 among them, do not add it to the 'kill' list, as it will be
* handled by the 'clean up' code which commits this transaction.
*
* I'm not using the SysCache, because this will happen but
* 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() */
OldHeap = heap_openr(oldrelname);
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 */
if (!RelationIsValid(OldIndex)) {
elog(WARN, "cluster: unknown index: \"%s\"",
oldindexname);
}
OIDOldIndex = OldIndex->rd_id; /* OID for the index scan */
heap_close(OldHeap);
index_close(OldIndex);
/*
* I need to build the copies of the heap and the index. The Commit()
* 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
* in the tables for the entire duration of this process with a pg_vlock.
*/
NewHeap = copy_heap(OIDOldHeap);
OIDNewHeap = NewHeap->rd_id;
strcpy(NewHeapName,NewHeap->rd_rel->relname.data);
/*
* Save the old names because they will get lost when the old
* relations are destroyed.
*/
strcpy(saveoldrelname, oldrelname);
strcpy(saveoldindexname, oldindexname);
/*
* 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
* among them, do not add it to the 'kill' list, as it will be handled
* by the 'clean up' code which commits this transaction.
*
* I'm not using the SysCache, because this will happen but 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() */
OldHeap = heap_openr(oldrelname);
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 */
if (!RelationIsValid(OldIndex))
{
elog(WARN, "cluster: unknown index: \"%s\"",
oldindexname);
}
OIDOldIndex = OldIndex->rd_id; /* OID for the index scan */
heap_close(OldHeap);
index_close(OldIndex);
/*
* I need to build the copies of the heap and the index. The Commit()
* 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 in the tables for the entire duration of this process
* 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). */
CommandCounterIncrement();
rebuildheap(OIDNewHeap, OIDOldHeap, OIDOldIndex);
/* To flush the filled new heap (and the statistics about it). */
CommandCounterIncrement();
/* To make the new heap visible (which is until now empty). */
CommandCounterIncrement();
/* Create new index over the tuples of the new heap. */
copy_index(OIDOldIndex, OIDNewHeap);
sprintf(NewIndexName, "temp_%x", OIDOldIndex);
/*
* make this really happen. Flush all the buffers.
* (Believe me, it is necessary ... ended up in a mess without it.)
*/
CommitTransactionCommand();
StartTransactionCommand();
rebuildheap(OIDNewHeap, OIDOldHeap, OIDOldIndex);
/* To flush the filled new heap (and the statistics about it). */
CommandCounterIncrement();
/* Create new index over the tuples of the new heap. */
copy_index(OIDOldIndex, OIDNewHeap);
sprintf(NewIndexName, "temp_%x", OIDOldIndex);
/*
* make this really happen. Flush all the buffers. (Believe me, it is
* necessary ... ended up in a mess without it.)
*/
CommitTransactionCommand();
StartTransactionCommand();
/* Destroy old heap (along with its index) and rename new. */
heap_destroy(oldrelname);
renamerel(NewHeapName, saveoldrelname);
TypeRename(NewHeapName, saveoldrelname);
/* Destroy old heap (along with its index) and rename new. */
heap_destroy(oldrelname);
renamerel(NewIndexName, saveoldindexname);
renamerel(NewHeapName, saveoldrelname);
TypeRename(NewHeapName, saveoldrelname);
/*
* Again flush all the buffers.
*/
CommitTransactionCommand();
StartTransactionCommand();
renamerel(NewIndexName, saveoldindexname);
/*
* Again flush all the buffers.
*/
CommitTransactionCommand();
StartTransactionCommand();
}
static Relation
static Relation
copy_heap(Oid OIDOldHeap)
{
char NewName[NAMEDATALEN];
TupleDesc OldHeapDesc, tupdesc;
Oid OIDNewHeap;
Relation NewHeap, OldHeap;
char NewName[NAMEDATALEN];
TupleDesc OldHeapDesc,
tupdesc;
Oid OIDNewHeap;
Relation NewHeap,
OldHeap;
/*
* Create a new heap relation with a temporary name, which has the
* same tuple description as the old one.
*/
sprintf(NewName,"temp_%x", OIDOldHeap);
/*
* Create a new heap relation with a temporary name, which has the
* same tuple description as the old one.
*/
sprintf(NewName, "temp_%x", OIDOldHeap);
OldHeap= heap_open(OIDOldHeap);
OldHeapDesc= RelationGetTupleDescriptor(OldHeap);
OldHeap = heap_open(OIDOldHeap);
OldHeapDesc = RelationGetTupleDescriptor(OldHeap);
/*
* Need to make a copy of the tuple descriptor, heap_create modifies
* it.
*/
/*
* Need to make a copy of the tuple descriptor, heap_create modifies
* it.
*/
tupdesc = CreateTupleDescCopy(OldHeapDesc);
OIDNewHeap=heap_create(NewName,
NULL,
OldHeap->rd_rel->relarch,
OldHeap->rd_rel->relsmgr,
tupdesc);
tupdesc = CreateTupleDescCopy(OldHeapDesc);
if (!OidIsValid(OIDNewHeap))
elog(WARN,"clusterheap: cannot create temporary heap relation\n");
OIDNewHeap = heap_create(NewName,
NULL,
OldHeap->rd_rel->relarch,
OldHeap->rd_rel->relsmgr,
tupdesc);
NewHeap=heap_open(OIDNewHeap);
if (!OidIsValid(OIDNewHeap))
elog(WARN, "clusterheap: cannot create temporary heap relation\n");
heap_close(NewHeap);
heap_close(OldHeap);
NewHeap = heap_open(OIDNewHeap);
return NewHeap;
heap_close(NewHeap);
heap_close(OldHeap);
return NewHeap;
}
static void
copy_index(Oid OIDOldIndex, Oid OIDNewHeap)
{
Relation OldIndex, NewHeap;
HeapTuple Old_pg_index_Tuple, Old_pg_index_relation_Tuple, pg_proc_Tuple;
IndexTupleForm Old_pg_index_Form;
Form_pg_class Old_pg_index_relation_Form;
Form_pg_proc pg_proc_Form;
char *NewIndexName;
AttrNumber *attnumP;
int natts;
FuncIndexInfo * finfo;
Relation OldIndex,
NewHeap;
HeapTuple Old_pg_index_Tuple,
Old_pg_index_relation_Tuple,
pg_proc_Tuple;
IndexTupleForm Old_pg_index_Form;
Form_pg_class Old_pg_index_relation_Form;
Form_pg_proc pg_proc_Form;
char *NewIndexName;
AttrNumber *attnumP;
int natts;
FuncIndexInfo *finfo;
NewHeap = heap_open(OIDNewHeap);
OldIndex = index_open(OIDOldIndex);
NewHeap = heap_open(OIDNewHeap);
OldIndex = index_open(OIDOldIndex);
/*
* OK. Create a new (temporary) index for the one that's already
* here. To do this I get the info from pg_index, re-build the
* FunctInfo if I have to, and add a new index with a temporary
* name.
*/
Old_pg_index_Tuple =
SearchSysCacheTuple(INDEXRELID,
ObjectIdGetDatum(OldIndex->rd_id),
0,0,0);
/*
* OK. Create a new (temporary) index for the one that's already here.
* To do this I get the info from pg_index, re-build the FunctInfo if
* I have to, and add a new index with a temporary name.
*/
Old_pg_index_Tuple =
SearchSysCacheTuple(INDEXRELID,
ObjectIdGetDatum(OldIndex->rd_id),
0, 0, 0);
Assert(Old_pg_index_Tuple);
Old_pg_index_Form = (IndexTupleForm)GETSTRUCT(Old_pg_index_Tuple);
Assert(Old_pg_index_Tuple);
Old_pg_index_Form = (IndexTupleForm) GETSTRUCT(Old_pg_index_Tuple);
Old_pg_index_relation_Tuple =
SearchSysCacheTuple(RELOID,
ObjectIdGetDatum(OldIndex->rd_id),
0,0,0);
Old_pg_index_relation_Tuple =
SearchSysCacheTuple(RELOID,
ObjectIdGetDatum(OldIndex->rd_id),
0, 0, 0);
Assert(Old_pg_index_relation_Tuple);
Old_pg_index_relation_Form =
(Form_pg_class)GETSTRUCT(Old_pg_index_relation_Tuple);
Assert(Old_pg_index_relation_Tuple);
Old_pg_index_relation_Form =
(Form_pg_class) GETSTRUCT(Old_pg_index_relation_Tuple);
NewIndexName = palloc(NAMEDATALEN); /* XXX */
sprintf(NewIndexName, "temp_%x", OIDOldIndex); /* Set the name. */
NewIndexName = palloc(NAMEDATALEN); /* XXX */
sprintf(NewIndexName, "temp_%x", OIDOldIndex); /* Set the name. */
/*
* 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
* I've got to be sure.
*/
for (attnumP = &(Old_pg_index_Form->indkey[0]), natts = 0;
*attnumP != InvalidAttrNumber;
attnumP++, natts++);
/*
* 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 I've
* got to be sure.
*/
for (attnumP = &(Old_pg_index_Form->indkey[0]), natts = 0;
*attnumP != InvalidAttrNumber;
attnumP++, natts++);
/*
* If this is a functional index, I need to rebuild the functional
* component to pass it to the defining procedure.
*/
if (Old_pg_index_Form->indproc != InvalidOid) {
finfo = (FuncIndexInfo *) palloc(sizeof(FuncIndexInfo));
FIgetnArgs(finfo) = natts;
FIgetProcOid(finfo) = Old_pg_index_Form->indproc;
/*
* If this is a functional index, I need to rebuild the functional
* component to pass it to the defining procedure.
*/
if (Old_pg_index_Form->indproc != InvalidOid)
{
finfo = (FuncIndexInfo *) palloc(sizeof(FuncIndexInfo));
FIgetnArgs(finfo) = natts;
FIgetProcOid(finfo) = Old_pg_index_Form->indproc;
pg_proc_Tuple =
SearchSysCacheTuple(PROOID,
ObjectIdGetDatum(Old_pg_index_Form->indproc),
0,0,0);
pg_proc_Tuple =
SearchSysCacheTuple(PROOID,
ObjectIdGetDatum(Old_pg_index_Form->indproc),
0, 0, 0);
Assert(pg_proc_Tuple);
pg_proc_Form = (Form_pg_proc)GETSTRUCT(pg_proc_Tuple);
namecpy(&(finfo->funcName), &(pg_proc_Form->proname));
} else {
finfo = (FuncIndexInfo *) NULL;
natts = 1;
}
Assert(pg_proc_Tuple);
pg_proc_Form = (Form_pg_proc) GETSTRUCT(pg_proc_Tuple);
namecpy(&(finfo->funcName), &(pg_proc_Form->proname));
}
else
{
finfo = (FuncIndexInfo *) NULL;
natts = 1;
}
index_create((NewHeap->rd_rel->relname).data,
NewIndexName,
finfo,
NULL, /* type info is in the old index */
Old_pg_index_relation_Form->relam,
natts,
Old_pg_index_Form->indkey,
Old_pg_index_Form->indclass,
(uint16)0, (Datum) NULL, NULL,
Old_pg_index_Form->indislossy,
Old_pg_index_Form->indisunique);
index_create((NewHeap->rd_rel->relname).data,
NewIndexName,
finfo,
NULL, /* type info is in the old index */
Old_pg_index_relation_Form->relam,
natts,
Old_pg_index_Form->indkey,
Old_pg_index_Form->indclass,
(uint16) 0, (Datum) NULL, NULL,
Old_pg_index_Form->indislossy,
Old_pg_index_Form->indisunique);
heap_close(OldIndex);
heap_close(NewHeap);
heap_close(OldIndex);
heap_close(NewHeap);
}
static void
rebuildheap(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
{
Relation LocalNewHeap, LocalOldHeap, LocalOldIndex;
IndexScanDesc ScanDesc;
RetrieveIndexResult ScanResult;
ItemPointer HeapTid;
HeapTuple LocalHeapTuple;
Buffer LocalBuffer;
Oid OIDNewHeapInsert;
Relation LocalNewHeap,
LocalOldHeap,
LocalOldIndex;
IndexScanDesc ScanDesc;
RetrieveIndexResult ScanResult;
ItemPointer HeapTid;
HeapTuple LocalHeapTuple;
Buffer LocalBuffer;
Oid OIDNewHeapInsert;
/*
* Open the relations I need. Scan through the OldHeap on the OldIndex and
* insert each tuple into the NewHeap.
*/
LocalNewHeap=(Relation)heap_open(OIDNewHeap);
LocalOldHeap=(Relation)heap_open(OIDOldHeap);
LocalOldIndex=(Relation)index_open(OIDOldIndex);
/*
* Open the relations I need. Scan through the OldHeap on the OldIndex
* and insert each tuple into the NewHeap.
*/
LocalNewHeap = (Relation) heap_open(OIDNewHeap);
LocalOldHeap = (Relation) heap_open(OIDOldHeap);
LocalOldIndex = (Relation) index_open(OIDOldIndex);
ScanDesc=index_beginscan(LocalOldIndex, false, 0, (ScanKey) NULL);
ScanDesc = index_beginscan(LocalOldIndex, false, 0, (ScanKey) NULL);
while ((ScanResult =
index_getnext(ScanDesc, ForwardScanDirection)) != NULL) {
while ((ScanResult =
index_getnext(ScanDesc, ForwardScanDirection)) != NULL)
{
HeapTid = &ScanResult->heap_iptr;
LocalHeapTuple = heap_fetch(LocalOldHeap, 0, HeapTid, &LocalBuffer);
OIDNewHeapInsert =
heap_insert(LocalNewHeap, LocalHeapTuple);
pfree(ScanResult);
ReleaseBuffer(LocalBuffer);
}
index_endscan(ScanDesc);
HeapTid = &ScanResult->heap_iptr;
LocalHeapTuple = heap_fetch(LocalOldHeap, 0, HeapTid, &LocalBuffer);
OIDNewHeapInsert =
heap_insert(LocalNewHeap, LocalHeapTuple);
pfree(ScanResult);
ReleaseBuffer(LocalBuffer);
}
index_endscan(ScanDesc);
index_close(LocalOldIndex);
heap_close(LocalOldHeap);
heap_close(LocalNewHeap);
index_close(LocalOldIndex);
heap_close(LocalOldHeap);
heap_close(LocalNewHeap);
}

View File

@ -1,29 +1,29 @@
/*-------------------------------------------------------------------------
*
* 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
*
*
* 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
* The PortalExecutorHeapMemory crap needs to be eliminated
* by designing a better executor / portal processing memory
* interface.
*
* The PerformAddAttribute() code, like most of the relation
* manipulating code in the commands/ directory, should go
* someplace closer to the lib/catalog code.
*
* The PortalExecutorHeapMemory crap needs to be eliminated
* by designing a better executor / portal processing memory
* interface.
*
* The PerformAddAttribute() code, like most of the relation
* manipulating code in the commands/ directory, should go
* someplace closer to the lib/catalog code.
*
*-------------------------------------------------------------------------
*/
#include <postgres.h>
#include <access/relscan.h>
#include <utils/portal.h>
#include <utils/portal.h>
#include <commands/command.h>
#include <utils/mcxt.h>
#include <executor/executor.h>
@ -31,7 +31,7 @@
#include <catalog/indexing.h>
#include <utils/syscache.h>
#include <catalog/catalog.h>
#include <access/heapam.h>
#include <access/heapam.h>
#include <utils/array.h>
#include <utils/acl.h>
#include <optimizer/prep.h>
@ -41,443 +41,468 @@
#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
PortalCleanup(Portal portal)
{
MemoryContext context;
/* ----------------
* sanity checks
* ----------------
*/
AssertArg(PortalIsValid(portal));
AssertArg(portal->cleanup == PortalCleanup);
/* ----------------
* set proper portal-executor context before calling ExecMain.
* ----------------
*/
context = MemoryContextSwitchTo((MemoryContext) PortalGetHeapMemory(portal));
PortalExecutorHeapMemory = (MemoryContext)
PortalGetHeapMemory(portal);
/* ----------------
* tell the executor to shutdown the query
* ----------------
*/
ExecutorEnd(PortalGetQueryDesc(portal), PortalGetState(portal));
/* ----------------
* switch back to previous context
* ----------------
*/
MemoryContextSwitchTo(context);
PortalExecutorHeapMemory = (MemoryContext) NULL;
MemoryContext context;
/* ----------------
* sanity checks
* ----------------
*/
AssertArg(PortalIsValid(portal));
AssertArg(portal->cleanup == PortalCleanup);
/* ----------------
* set proper portal-executor context before calling ExecMain.
* ----------------
*/
context = MemoryContextSwitchTo((MemoryContext) PortalGetHeapMemory(portal));
PortalExecutorHeapMemory = (MemoryContext)
PortalGetHeapMemory(portal);
/* ----------------
* tell the executor to shutdown the query
* ----------------
*/
ExecutorEnd(PortalGetQueryDesc(portal), PortalGetState(portal));
/* ----------------
* switch back to previous context
* ----------------
*/
MemoryContextSwitchTo(context);
PortalExecutorHeapMemory = (MemoryContext) NULL;
}
/* --------------------------------
* PerformPortalFetch
* PerformPortalFetch
* --------------------------------
*/
void
PerformPortalFetch(char *name,
bool forward,
int count,
char *tag,
CommandDest dest)
bool forward,
int count,
char *tag,
CommandDest dest)
{
Portal portal;
int feature;
QueryDesc *queryDesc;
MemoryContext context;
/* ----------------
* sanity checks
* ----------------
*/
if (name == NULL) {
elog(NOTICE, "PerformPortalFetch: blank portal unsupported");
return;
}
/* ----------------
* get the portal from the portal name
* ----------------
*/
portal = GetPortalByName(name);
if (! PortalIsValid(portal)) {
elog(NOTICE, "PerformPortalFetch: portal \"%s\" not found",
name);
return;
}
/* ----------------
* switch into the portal context
* ----------------
*/
context= MemoryContextSwitchTo((MemoryContext)PortalGetHeapMemory(portal));
AssertState(context ==
(MemoryContext)PortalGetHeapMemory(GetPortalByName(NULL)));
/* ----------------
* setup "feature" to tell the executor what direction and
* how many tuples to fetch.
* ----------------
*/
if (forward)
feature = EXEC_FOR;
else
feature = EXEC_BACK;
/* ----------------
* tell the destination to prepare to recieve some tuples
* ----------------
*/
queryDesc = PortalGetQueryDesc(portal);
BeginCommand(name,
queryDesc->operation,
portal->attinfo,/* QueryDescGetTypeInfo(queryDesc), */
false, /* portal fetches don't end up in relations */
false, /* this is a portal fetch, not a "retrieve portal" */
tag,
dest);
/* ----------------
* execute the portal fetch operation
* ----------------
*/
PortalExecutorHeapMemory = (MemoryContext)
PortalGetHeapMemory(portal);
ExecutorRun(queryDesc, PortalGetState(portal), feature, count);
/* ----------------
* Note: the "end-of-command" tag is returned by higher-level
* utility code
*
* Return blank portal for now.
* Otherwise, this named portal will be cleaned.
* Note: portals will only be supported within a BEGIN...END
* block in the near future. Later, someone will fix it to
* do what is possible across transaction boundries.
* ----------------
*/
MemoryContextSwitchTo(
(MemoryContext)PortalGetHeapMemory(GetPortalByName(NULL)));
Portal portal;
int feature;
QueryDesc *queryDesc;
MemoryContext context;
/* ----------------
* sanity checks
* ----------------
*/
if (name == NULL)
{
elog(NOTICE, "PerformPortalFetch: blank portal unsupported");
return;
}
/* ----------------
* get the portal from the portal name
* ----------------
*/
portal = GetPortalByName(name);
if (!PortalIsValid(portal))
{
elog(NOTICE, "PerformPortalFetch: portal \"%s\" not found",
name);
return;
}
/* ----------------
* switch into the portal context
* ----------------
*/
context = MemoryContextSwitchTo((MemoryContext) PortalGetHeapMemory(portal));
AssertState(context ==
(MemoryContext) PortalGetHeapMemory(GetPortalByName(NULL)));
/* ----------------
* setup "feature" to tell the executor what direction and
* how many tuples to fetch.
* ----------------
*/
if (forward)
feature = EXEC_FOR;
else
feature = EXEC_BACK;
/* ----------------
* tell the destination to prepare to recieve some tuples
* ----------------
*/
queryDesc = PortalGetQueryDesc(portal);
BeginCommand(name,
queryDesc->operation,
portal->attinfo, /* QueryDescGetTypeInfo(queryDesc),
* */
false, /* portal fetches don't end up in
* relations */
false, /* this is a portal fetch, not a "retrieve
* portal" */
tag,
dest);
/* ----------------
* execute the portal fetch operation
* ----------------
*/
PortalExecutorHeapMemory = (MemoryContext)
PortalGetHeapMemory(portal);
ExecutorRun(queryDesc, PortalGetState(portal), feature, count);
/* ----------------
* Note: the "end-of-command" tag is returned by higher-level
* utility code
*
* Return blank portal for now.
* Otherwise, this named portal will be cleaned.
* Note: portals will only be supported within a BEGIN...END
* block in the near future. Later, someone will fix it to
* do what is possible across transaction boundries.
* ----------------
*/
MemoryContextSwitchTo(
(MemoryContext) PortalGetHeapMemory(GetPortalByName(NULL)));
}
/* --------------------------------
* PerformPortalClose
* PerformPortalClose
* --------------------------------
*/
void
PerformPortalClose(char *name, CommandDest dest)
{
Portal portal;
/* ----------------
* sanity checks
* ----------------
*/
if (name == NULL) {
elog(NOTICE, "PerformPortalClose: blank portal unsupported");
return;
}
/* ----------------
* get the portal from the portal name
* ----------------
*/
portal = GetPortalByName(name);
if (! PortalIsValid(portal)) {
elog(NOTICE, "PerformPortalClose: portal \"%s\" not found",
name);
return;
}
/* ----------------
* Note: PortalCleanup is called as a side-effect
* ----------------
*/
PortalDestroy(&portal);
Portal portal;
/* ----------------
* sanity checks
* ----------------
*/
if (name == NULL)
{
elog(NOTICE, "PerformPortalClose: blank portal unsupported");
return;
}
/* ----------------
* get the portal from the portal name
* ----------------
*/
portal = GetPortalByName(name);
if (!PortalIsValid(portal))
{
elog(NOTICE, "PerformPortalClose: portal \"%s\" not found",
name);
return;
}
/* ----------------
* Note: PortalCleanup is called as a side-effect
* ----------------
*/
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
* is given attnums in sequential order and is added to the
* ATTRIBUTE relation. If the AMI fails, defunct tuples will
* remain in the ATTRIBUTE relation for later vacuuming.
* Later, there may be some reserved attribute names???
* Adds attribute field(s) to a relation. Each new attribute
* is given attnums in sequential order and is added to the
* ATTRIBUTE relation. If the AMI fails, defunct tuples will
* remain in the ATTRIBUTE relation for later vacuuming.
* 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:
* Initial idea of ordering the tuple attributes so that all
* the variable length domains occured last was scratched. Doing
* so would not speed access too much (in general) and would create
* many complications in formtuple, amgetattr, and addattribute.
* Note:
* Initial idea of ordering the tuple attributes so that all
* the variable length domains occured last was scratched. Doing
* so would not speed access too much (in general) and would create
* many complications in formtuple, amgetattr, and addattribute.
*
* scan attribute catalog for name conflict (within rel)
* scan type catalog for absence of data type (if not arg)
* create attnum magically???
* create attribute tuple
* insert attribute in attribute catalog
* modify reldesc
* create new relation tuple
* insert new relation in relation catalog
* delete original relation from relation catalog
* scan attribute catalog for name conflict (within rel)
* scan type catalog for absence of data type (if not arg)
* create attnum magically???
* create attribute tuple
* insert attribute in attribute catalog
* modify reldesc
* create new relation tuple
* insert new relation in relation catalog
* delete original relation from relation catalog
* ----------------
*/
void
PerformAddAttribute(char *relationName,
char *userName,
bool inherits,
ColumnDef *colDef)
{
Relation relrdesc, attrdesc;
HeapScanDesc attsdesc;
HeapTuple reltup;
HeapTuple attributeTuple;
AttributeTupleForm attribute;
FormData_pg_attribute attributeD;
int i;
int minattnum, maxatts;
HeapTuple tup;
ScanKeyData key[2];
ItemPointerData oldTID;
Relation idescs[Num_pg_attr_indices];
Relation ridescs[Num_pg_class_indices];
bool hasindex;
/*
* permissions checking. this would normally be done in utility.c,
* but this particular routine is recursive.
*
* normally, only the owner of a class can change its schema.
*/
if (IsSystemRelationName(relationName))
elog(WARN, "PerformAddAttribute: class \"%s\" is a system catalog",
relationName);
char *userName,
bool inherits,
ColumnDef * colDef)
{
Relation relrdesc,
attrdesc;
HeapScanDesc attsdesc;
HeapTuple reltup;
HeapTuple attributeTuple;
AttributeTupleForm attribute;
FormData_pg_attribute attributeD;
int i;
int minattnum,
maxatts;
HeapTuple tup;
ScanKeyData key[2];
ItemPointerData oldTID;
Relation idescs[Num_pg_attr_indices];
Relation ridescs[Num_pg_class_indices];
bool hasindex;
/*
* permissions checking. this would normally be done in utility.c,
* but this particular routine is recursive.
*
* normally, only the owner of a class can change its schema.
*/
if (IsSystemRelationName(relationName))
elog(WARN, "PerformAddAttribute: class \"%s\" is a system catalog",
relationName);
#ifndef NO_SECURITY
if (!pg_ownercheck(userName, relationName, RELNAME))
elog(WARN, "PerformAddAttribute: you do not own class \"%s\"",
relationName);
if (!pg_ownercheck(userName, relationName, RELNAME))
elog(WARN, "PerformAddAttribute: you do not own class \"%s\"",
relationName);
#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)) {
elog(WARN, "PerformAddAttribute: unknown relation: \"%s\"",
relationName);
}
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) {
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);
/*
* 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))
{
elog(WARN, "PerformAddAttribute: unknown relation: \"%s\"",
relationName);
}
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)
{
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);
}
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;
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;
if (((Form_pg_class) GETSTRUCT(reltup))->relkind == RELKIND_INDEX)
{
elog(WARN, "PerformAddAttribute: index relation \"%s\" not changed",
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
*/
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);
hasindex = RelationGetRelationTupleForm(attrdesc)->relhasindex;
if (hasindex)
CatalogIndexInsert(idescs,
Num_pg_attr_indices,
attrdesc,
attributeTuple);
}
CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
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);
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
*/
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

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 @@
/*-------------------------------------------------------------------------
*
* explain.c--
* Explain the query execution plan
* Explain the query execution plan
*
* Copyright (c) 1994-5, Regents of the University of California
*
*
* 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 <parser/catalog_utils.h>
#include <parser/parse_query.h> /* for MakeTimeRange() */
#include <parser/parse_query.h> /* for MakeTimeRange() */
#include <nodes/plannodes.h>
#include <tcop/tcopprot.h>
#include <lib/stringinfo.h>
@ -25,79 +25,86 @@
#include <optimizer/planner.h>
#include <access/xact.h>
typedef struct ExplainState {
/* options */
bool printCost; /* print cost */
bool printNodes; /* do nodeToString() instead */
/* other states */
List *rtable; /* range table */
} ExplainState;
typedef struct ExplainState
{
/* options */
bool printCost; /* print cost */
bool printNodes; /* do nodeToString() instead */
/* other states */
List *rtable; /* range table */
} ExplainState;
static char *Explain_PlanToString(Plan *plan, ExplainState *es);
static char *Explain_PlanToString(Plan * plan, ExplainState * es);
/*
* ExplainQuery -
* print out the execution plan for a given query
* print out the execution plan for a given query
*
*/
void
ExplainQuery(Query *query, bool verbose, CommandDest dest)
ExplainQuery(Query * query, bool verbose, CommandDest dest)
{
char *s = NULL, *s2;
Plan *plan;
ExplainState *es;
int len;
char *s = NULL,
*s2;
Plan *plan;
ExplainState *es;
int len;
if (IsAbortedTransactionBlockState()) {
char *tag = "*ABORT STATE*";
EndCommand(tag, dest);
elog(NOTICE, "(transaction aborted): %s",
"queries ignored until END");
return;
}
if (IsAbortedTransactionBlockState())
{
char *tag = "*ABORT STATE*";
/* plan the queries (XXX we've ignored rewrite!!) */
plan = planner(query);
EndCommand(tag, dest);
/* pg_plan could have failed */
if (plan == NULL)
return;
elog(NOTICE, "(transaction aborted): %s",
"queries ignored until END");
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);
return;
}
}
/* 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);
/* plan the queries (XXX we've ignored rewrite!!) */
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 */
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 -
* converts a Node into ascii string and append it to 'str'
* converts a Node into ascii string and append it to 'str'
*/
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 buf[1000];
int i;
if (plan==NULL) {
appendStringInfo(str, "\n");
return;
}
char *pname;
char buf[1000];
int i;
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);
if (plan == NULL)
{
appendStringInfo(str, "\n");
return;
}
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);
}
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;
}
/* righttree */
if (innerPlan(plan)) {
for(i=0; i < indent; i++)
appendStringInfo(str, " ");
appendStringInfo(str, " -> ");
explain_outNode(str, innerPlan(plan), indent+1, es);
}
return;
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 */
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 *
Explain_PlanToString(Plan *plan, ExplainState *es)
static char *
Explain_PlanToString(Plan * plan, ExplainState * es)
{
StringInfo str;
char *s;
if (plan==NULL)
return "";
Assert(plan!=NULL);
str = makeStringInfo();
explain_outNode(str, plan, 0, es);
s = str->data;
pfree(str);
StringInfo str;
char *s;
return s;
if (plan == NULL)
return "";
Assert(plan != NULL);
str = makeStringInfo();
explain_outNode(str, plan, 0, es);
s = str->data;
pfree(str);
return s;
}

View File

@ -1,17 +1,17 @@
/*-------------------------------------------------------------------------
*
* purge.c--
* the POSTGRES purge command.
* the POSTGRES purge command.
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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:
* XXX There are many instances of int32 instead of ...Time. These
* should be changed once it is decided the signed'ness will be.
* XXX There are many instances of int32 instead of ...Time. These
* should be changed once it is decided the signed'ness will be.
*
*-------------------------------------------------------------------------
*/
@ -21,145 +21,156 @@
#include <access/heapam.h>
#include <access/xact.h>
#include <utils/tqual.h> /* for NowTimeQual */
#include <utils/tqual.h> /* for NowTimeQual */
#include <catalog/catname.h>
#include <catalog/indexing.h>
#include <fmgr.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 ABSOLUTE 02
#define RELATIVE 01
#define ABSOLUTE 02
int32
RelationPurge(char *relationName,
char *absoluteTimeString,
char *relativeTimeString)
char *absoluteTimeString,
char *relativeTimeString)
{
register i;
AbsoluteTime absoluteTime = INVALID_ABSTIME;
RelativeTime relativeTime = INVALID_RELTIME;
bits8 dateTag;
Relation relation;
HeapScanDesc scan;
static ScanKeyData key[1] = {
{ 0, Anum_pg_class_relname, F_NAMEEQ }
};
Buffer buffer;
HeapTuple newTuple, oldTuple;
AbsoluteTime currentTime;
char *values[Natts_pg_class];
char nulls[Natts_pg_class];
char replace[Natts_pg_class];
Relation idescs[Num_pg_class_indices];
/*
* XXX for some reason getmyrelids (in inval.c) barfs when
* you heap_replace tuples from these classes. i thought
* setheapoverride would fix it but it didn't. for now,
* just disallow purge on these classes.
*/
if (strcmp(RelationRelationName, relationName) == 0 ||
strcmp(AttributeRelationName, relationName) == 0 ||
strcmp(AccessMethodRelationName, relationName) == 0 ||
strcmp(AccessMethodOperatorRelationName, relationName) == 0) {
elog(WARN, "%s: cannot purge catalog \"%s\"",
cmdname, relationName);
}
if (PointerIsValid(absoluteTimeString)) {
absoluteTime = (int32) nabstimein(absoluteTimeString);
absoluteTimeString[0] = '\0';
if (absoluteTime == INVALID_ABSTIME) {
elog(NOTICE, "%s: bad absolute time string \"%s\"",
cmdname, absoluteTimeString);
elog(WARN, "purge not executed");
register i;
AbsoluteTime absoluteTime = INVALID_ABSTIME;
RelativeTime relativeTime = INVALID_RELTIME;
bits8 dateTag;
Relation relation;
HeapScanDesc scan;
static ScanKeyData key[1] = {
{0, Anum_pg_class_relname, F_NAMEEQ}
};
Buffer buffer;
HeapTuple newTuple,
oldTuple;
AbsoluteTime currentTime;
char *values[Natts_pg_class];
char nulls[Natts_pg_class];
char replace[Natts_pg_class];
Relation idescs[Num_pg_class_indices];
/*
* XXX for some reason getmyrelids (in inval.c) barfs when you
* heap_replace tuples from these classes. i thought setheapoverride
* would fix it but it didn't. for now, just disallow purge on these
* classes.
*/
if (strcmp(RelationRelationName, relationName) == 0 ||
strcmp(AttributeRelationName, relationName) == 0 ||
strcmp(AccessMethodRelationName, relationName) == 0 ||
strcmp(AccessMethodOperatorRelationName, relationName) == 0)
{
elog(WARN, "%s: cannot purge catalog \"%s\"",
cmdname, relationName);
}
}
#ifdef PURGEDEBUG
elog(DEBUG, "%s: absolute time `%s' is %d.",
cmdname, absoluteTimeString, absoluteTime);
#endif /* defined(PURGEDEBUG) */
if (PointerIsValid(relativeTimeString)) {
if (isreltime(relativeTimeString) != 1) {
elog(WARN, "%s: bad relative time string \"%s\"",
cmdname, relativeTimeString);
if (PointerIsValid(absoluteTimeString))
{
absoluteTime = (int32) nabstimein(absoluteTimeString);
absoluteTimeString[0] = '\0';
if (absoluteTime == INVALID_ABSTIME)
{
elog(NOTICE, "%s: bad absolute time string \"%s\"",
cmdname, absoluteTimeString);
elog(WARN, "purge not executed");
}
}
relativeTime = reltimein(relativeTimeString);
#ifdef PURGEDEBUG
elog(DEBUG, "%s: relative time `%s' is %d.",
cmdname, relativeTimeString, relativeTime);
#endif /* defined(PURGEDEBUG) */
}
/*
* Find the RELATION relation tuple for the given relation.
*/
relation = heap_openr(RelationRelationName);
key[0].sk_argument = PointerGetDatum(relationName);
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)) {
elog(DEBUG, "%s: absolute time `%s' is %d.",
cmdname, absoluteTimeString, absoluteTime);
#endif /* defined(PURGEDEBUG) */
if (PointerIsValid(relativeTimeString))
{
if (isreltime(relativeTimeString) != 1)
{
elog(WARN, "%s: bad relative time string \"%s\"",
cmdname, relativeTimeString);
}
relativeTime = reltimein(relativeTimeString);
#ifdef PURGEDEBUG
elog(DEBUG, "%s: relative time `%s' is %d.",
cmdname, relativeTimeString, relativeTime);
#endif /* defined(PURGEDEBUG) */
}
/*
* Find the RELATION relation tuple for the given relation.
*/
relation = heap_openr(RelationRelationName);
key[0].sk_argument = PointerGetDatum(relationName);
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);
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);
heap_endscan(scan);
heap_close(relation);
return(1);
return (1);
}

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,13 +1,13 @@
/*-------------------------------------------------------------------------
*
* rename.c--
* renameatt() and renamerel() reside here.
* renameatt() and renamerel() reside here.
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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_class.h>
#include <optimizer/internal.h>
#include <optimizer/prep.h> /* for find_all_inheritors */
#include <optimizer/prep.h> /* for find_all_inheritors */
#ifndef NO_SECURITY
# include <utils/acl.h>
#endif /* !NO_SECURITY */
#include <utils/acl.h>
#endif /* !NO_SECURITY */
#ifndef HAVE_MEMMOVE
# include <regex/utils.h>
#include <regex/utils.h>
#else
# include <string.h>
#include <string.h>
#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.
* No record of the previous attname is kept (correct?).
* Attname attribute is changed in attribute catalog.
* No record of the previous attname is kept (correct?).
*
* get proper reldesc from relation catalog (if not arg)
* scan attribute catalog
* for name conflict (within rel)
* for original attribute (if not arg)
* modify attname in attribute tuple
* insert modified attribute in attribute catalog
* delete original attribute from attribute catalog
* get proper reldesc from relation catalog (if not arg)
* scan attribute catalog
* for name conflict (within rel)
* for original attribute (if not arg)
* modify attname in attribute tuple
* insert modified attribute in attribute catalog
* delete original attribute from attribute catalog
*
* XXX Renaming an indexed attribute must (eventually) also change
* the attribute name in the associated indexes.
* XXX Renaming an indexed attribute must (eventually) also change
* the attribute name in the associated indexes.
*/
void
renameatt(char *relname,
char *oldattname,
char *newattname,
char *userName,
int recurse)
char *oldattname,
char *newattname,
char *userName,
int recurse)
{
Relation relrdesc, attrdesc;
HeapTuple reltup, oldatttup, newatttup;
ItemPointerData oldTID;
Relation idescs[Num_pg_attr_indices];
/*
* permissions checking. this would normally be done in utility.c,
* 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);
Relation relrdesc,
attrdesc;
HeapTuple reltup,
oldatttup,
newatttup;
ItemPointerData oldTID;
Relation idescs[Num_pg_attr_indices];
/*
* 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.
* permissions checking. this would normally be done in utility.c,
* but this particular routine is recursive.
*
* normally, only the owner of a class can change its schema.
*/
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! */
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
* 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);
reltup = ClassNameIndexScan(relrdesc, relname);
if (!PointerIsValid(reltup))
{
heap_close(relrdesc);
elog(WARN, "renameatt: relation \"%s\" nonexistent",
relname);
return;
}
}
relrdesc = heap_openr(RelationRelationName);
reltup = ClassNameIndexScan(relrdesc, relname);
if (!PointerIsValid(reltup)) {
heap_close(relrdesc);
elog(WARN, "renameatt: relation \"%s\" nonexistent",
relname);
return;
}
heap_close(relrdesc);
attrdesc = heap_openr(AttributeRelationName);
oldatttup = AttributeNameIndexScan(attrdesc, reltup->t_oid, oldattname);
if (!PointerIsValid(oldatttup)) {
attrdesc = heap_openr(AttributeRelationName);
oldatttup = AttributeNameIndexScan(attrdesc, reltup->t_oid, oldattname);
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);
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);
pfree(oldatttup);
}
/*
* renamerel - change the name of a relation
* renamerel - change the name of a relation
*
* Relname attribute is changed in relation catalog.
* No record of the previous relname is kept (correct?).
* Relname attribute is changed in relation catalog.
* No record of the previous relname is kept (correct?).
*
* scan relation catalog
* for name conflict
* for original relation (if not arg)
* modify relname in relation tuple
* insert modified relation in relation catalog
* delete original relation from relation catalog
* scan relation catalog
* for name conflict
* for original relation (if not arg)
* modify relname in relation tuple
* insert modified relation in relation catalog
* delete original relation from relation catalog
*
* XXX Will currently lose track of a relation if it is unable to
* properly replace the new relation tuple.
* XXX Will currently lose track of a relation if it is unable to
* properly replace the new relation tuple.
*/
void
renamerel(char oldrelname[], char newrelname[])
{
Relation relrdesc; /* for RELATION relation */
HeapTuple oldreltup, newreltup;
ItemPointerData oldTID;
char oldpath[MAXPGPATH], newpath[MAXPGPATH];
Relation idescs[Num_pg_class_indices];
if (IsSystemRelationName(oldrelname)) {
elog(WARN, "renamerel: system relation \"%s\" not renamed",
oldrelname);
return;
}
if (IsSystemRelationName(newrelname)) {
elog(WARN, "renamerel: Illegal class name: \"%s\" -- pg_ is reserved for system catalogs",
newrelname);
return;
}
relrdesc = heap_openr(RelationRelationName);
oldreltup = ClassNameIndexScan(relrdesc, oldrelname);
if (!PointerIsValid(oldreltup)) {
heap_close(relrdesc);
elog(WARN, "renamerel: relation \"%s\" does not exist",
oldrelname);
}
newreltup = ClassNameIndexScan(relrdesc, newrelname);
if (PointerIsValid(newreltup)) {
Relation relrdesc; /* for RELATION relation */
HeapTuple oldreltup,
newreltup;
ItemPointerData oldTID;
char oldpath[MAXPGPATH],
newpath[MAXPGPATH];
Relation idescs[Num_pg_class_indices];
if (IsSystemRelationName(oldrelname))
{
elog(WARN, "renamerel: system relation \"%s\" not renamed",
oldrelname);
return;
}
if (IsSystemRelationName(newrelname))
{
elog(WARN, "renamerel: Illegal class name: \"%s\" -- pg_ is reserved for system catalogs",
newrelname);
return;
}
relrdesc = heap_openr(RelationRelationName);
oldreltup = ClassNameIndexScan(relrdesc, oldrelname);
if (!PointerIsValid(oldreltup))
{
heap_close(relrdesc);
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);
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);
pfree(oldreltup);
heap_close(relrdesc);
}

View File

@ -1,7 +1,7 @@
/*-------------------------------------------------------------------------
*
* sequence.c--
* PostgreSQL sequences support code.
* PostgreSQL sequences support code.
*
*-------------------------------------------------------------------------
*/
@ -19,523 +19,539 @@
#include <commands/sequence.h>
#include <utils/builtins.h>
#define SEQ_MAGIC 0x1717
#define SEQ_MAGIC 0x1717
#define SEQ_MAXVALUE ((int4)0x7FFFFFFF)
#define SEQ_MINVALUE -(SEQ_MAXVALUE)
bool ItsSequenceCreation = false;
bool ItsSequenceCreation = false;
typedef struct FormData_pg_sequence {
NameData sequence_name;
int4 last_value;
int4 increment_by;
int4 max_value;
int4 min_value;
int4 cache_value;
char is_cycled;
char is_called;
} FormData_pg_sequence;
typedef struct FormData_pg_sequence
{
NameData sequence_name;
int4 last_value;
int4 increment_by;
int4 max_value;
int4 min_value;
int4 cache_value;
char is_cycled;
char is_called;
} FormData_pg_sequence;
typedef FormData_pg_sequence *SequenceTupleForm;
typedef FormData_pg_sequence *SequenceTupleForm;
typedef struct sequence_magic {
uint32 magic;
} sequence_magic;
typedef struct sequence_magic
{
uint32 magic;
} sequence_magic;
typedef struct SeqTableData {
char *name;
Oid relid;
typedef struct SeqTableData
{
char *name;
Oid relid;
Relation rel;
int4 cached;
int4 last;
int4 increment;
struct SeqTableData *next;
} SeqTableData;
struct SeqTableData *next;
} SeqTableData;
typedef SeqTableData *SeqTable;
static SeqTable seqtab = NULL;
static SeqTable init_sequence (char *caller, char *name);
static SequenceTupleForm read_info (char * caller, SeqTable elm, Buffer * buf);
static void init_params (CreateSeqStmt *seq, SequenceTupleForm new);
static int get_param (DefElem *def);
static SeqTable init_sequence(char *caller, char *name);
static SequenceTupleForm read_info(char *caller, SeqTable elm, Buffer * buf);
static void init_params(CreateSeqStmt * seq, SequenceTupleForm new);
static int get_param(DefElem * def);
/*
* DefineSequence --
* Creates a new sequence relation
* Creates a new sequence relation
*/
void
DefineSequence (CreateSeqStmt *seq)
DefineSequence(CreateSeqStmt * seq)
{
FormData_pg_sequence new;
CreateStmt *stmt = makeNode (CreateStmt);
ColumnDef *coldef;
TypeName *typnam;
Relation rel;
Buffer buf;
PageHeader page;
sequence_magic *sm;
HeapTuple tuple;
TupleDesc tupDesc;
Datum value[SEQ_COL_LASTCOL];
char null[SEQ_COL_LASTCOL];
int i;
FormData_pg_sequence new;
CreateStmt *stmt = makeNode(CreateStmt);
ColumnDef *coldef;
TypeName *typnam;
Relation rel;
Buffer buf;
PageHeader page;
sequence_magic *sm;
HeapTuple tuple;
TupleDesc tupDesc;
Datum value[SEQ_COL_LASTCOL];
char null[SEQ_COL_LASTCOL];
int i;
/* Check and set values */
init_params (seq, &new);
/* Check and set values */
init_params(seq, &new);
/*
* Create relation (and fill null[] & value[])
*/
stmt->tableElts = NIL;
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)
/*
* Create relation (and fill null[] & value[])
*/
stmt->tableElts = NIL;
for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++)
{
case SEQ_COL_NAME:
typnam->name = "name";
coldef->colname = "sequence_name";
value[i-1] = PointerGetDatum (seq->seqname);
break;
case SEQ_COL_LASTVAL:
typnam->name = "int4";
coldef->colname = "last_value";
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;
stmt->archiveLoc = -1; /* default */
stmt->archiveType = ARCH_NONE;
stmt->inhRelnames = NIL;
stmt->constraints = NIL;
ItsSequenceCreation = true; /* hack */
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] = ' ';
DefineRelation (stmt);
/* Xact abort calls CloseSequences, which turns ItsSequenceCreation off */
ItsSequenceCreation = false; /* hack */
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
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;
switch (i)
{
case SEQ_COL_NAME:
typnam->name = "name";
coldef->colname = "sequence_name";
value[i - 1] = PointerGetDatum(seq->seqname);
break;
case SEQ_COL_LASTVAL:
typnam->name = "int4";
coldef->colname = "last_value";
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);
}
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 */
elm->last = result; /* last returned number */
elm->cached = next; /* last cached number */
stmt->relname = seq->seqname;
stmt->archiveLoc = -1; /* default */
stmt->archiveType = ARCH_NONE;
stmt->inhRelnames = NIL;
stmt->constraints = NIL;
/* save info in sequence relation */
seq->last_value = next; /* last fetched number */
seq->is_called = 't';
if ( WriteBuffer (buf) == STATUS_ERROR )
elog (WARN, "%s.nextval: WriteBuffer failed", elm->name);
ItsSequenceCreation = true; /* hack */
ItemPointerSet(&iptr, 0, FirstOffsetNumber);
RelationUnsetSingleWLockPage (elm->rel, &iptr);
DefineRelation(stmt);
/*
* Xact abort calls CloseSequences, which turns ItsSequenceCreation
* off
*/
ItsSequenceCreation = false;/* hack */
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;
return (result);
}
int4
currval (struct varlena * seqin)
nextval(struct varlena * seqin)
{
char *seqname = textout(seqin);
SeqTable elm;
int4 result;
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 ("currval", seqname);
pfree (seqname);
if ( elm->increment == 0 ) /* nextval/read_info were not called */
{
elog (WARN, "%s.currval is not yet defined in this session", elm->name);
}
result = elm->last;
return (result);
}
/* open and WIntentLock sequence */
elm = init_sequence("nextval", seqname);
pfree(seqname);
static SequenceTupleForm
read_info (char * caller, SeqTable elm, Buffer * buf)
{
ItemPointerData iptr;
PageHeader page;
ItemId lp;
HeapTuple tuple;
sequence_magic *sm;
SequenceTupleForm seq;
if (elm->last != elm->cached) /* some numbers were cached */
{
elm->last += elm->increment;
return (elm->last);
}
ItemPointerSet(&iptr, 0, FirstOffsetNumber);
RelationSetSingleWLockPage (elm->rel, &iptr);
seq = read_info("nextval", elm, &buf); /* lock page and read
* tuple */
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);
next = result = seq->last_value;
incby = seq->increment_by;
maxv = seq->max_value;
minv = seq->min_value;
cache = seq->cache_value;
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);
if (seq->is_called != 't')
rescnt++; /* last_value if not called */
lp = PageGetItemId (page, FirstOffsetNumber);
Assert (ItemIdIsUsed (lp));
tuple = (HeapTuple) PageGetItem ((Page) page, lp);
seq = (SequenceTupleForm) GETSTRUCT(tuple);
elm->increment = seq->increment_by;
while (rescnt < cache) /* try to fetch cache numbers */
{
return (seq);
/*
* 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 */
elm->last = result; /* last returned number */
elm->cached = next; /* last cached number */
/* save info in sequence relation */
seq->last_value = next; /* last fetched number */
seq->is_called = 't';
if (WriteBuffer(buf) == STATUS_ERROR)
elog(WARN, "%s.nextval: WriteBuffer failed", elm->name);
ItemPointerSet(&iptr, 0, FirstOffsetNumber);
RelationUnsetSingleWLockPage(elm->rel, &iptr);
return (result);
}
static SeqTable
init_sequence (char * caller, char * name)
int4
currval(struct varlena * seqin)
{
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);
char *seqname = textout(seqin);
SeqTable elm;
int4 result;
if ( ! RelationIsValid (temp->rel) )
elog (WARN, "%s.%s: sequence does not exist", name, caller);
/* open and WIntentLock sequence */
elm = init_sequence("currval", seqname);
pfree(seqname);
RelationSetWIntentLock (temp->rel);
if ( temp->rel->rd_rel->relkind != RELKIND_SEQUENCE )
elog (WARN, "%s.%s: %s is not sequence !", name, caller, name);
if (elm->increment == 0) /* nextval/read_info were not called */
{
elog(WARN, "%s.currval is not yet defined in this session", elm->name);
}
result = elm->last;
return (result);
}
static SequenceTupleForm
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);
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);
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 --
* is calling by xact mgr at commit/abort.
* is calling by xact mgr at commit/abort.
*/
void
CloseSequences (void)
CloseSequences(void)
{
SeqTable elm;
Relation rel;
ItsSequenceCreation = false;
for (elm = seqtab; elm != (SeqTable) NULL; )
{
if ( elm->rel != (Relation) NULL ) /* opened in current xact */
{
rel = elm->rel;
elm->rel = (Relation) NULL;
RelationUnsetWIntentLock (rel);
heap_close (rel);
}
elm = elm->next;
}
SeqTable elm;
Relation rel;
ItsSequenceCreation = false;
for (elm = seqtab; elm != (SeqTable) NULL;)
{
if (elm->rel != (Relation) NULL) /* opened in current xact */
{
rel = elm->rel;
elm->rel = (Relation) NULL;
RelationUnsetWIntentLock(rel);
heap_close(rel);
}
elm = elm->next;
}
return;
return;
}
static void
init_params (CreateSeqStmt *seq, SequenceTupleForm new)
static void
init_params(CreateSeqStmt * seq, SequenceTupleForm new)
{
DefElem *last_value = NULL;
DefElem *increment_by = NULL;
DefElem *max_value = NULL;
DefElem *min_value = NULL;
DefElem *cache_value = NULL;
List *option;
new->is_cycled = 'f';
foreach (option, seq->options)
{
DefElem *defel = (DefElem *)lfirst(option);
if ( !strcasecmp(defel->defname, "increment") )
increment_by = defel;
else if ( !strcasecmp(defel->defname, "start") )
last_value = defel;
else if ( !strcasecmp(defel->defname, "maxvalue") )
max_value = defel;
else if ( !strcasecmp(defel->defname, "minvalue") )
min_value = defel;
else if ( !strcasecmp(defel->defname, "cache") )
cache_value = defel;
else if ( !strcasecmp(defel->defname, "cycle") )
{
if ( defel->arg != (Node*) NULL )
elog (WARN, "DefineSequence: CYCLE ??");
new->is_cycled = 't';
}
else
elog (WARN, "DefineSequence: option \"%s\" not recognized",
defel->defname);
}
DefElem *last_value = NULL;
DefElem *increment_by = NULL;
DefElem *max_value = NULL;
DefElem *min_value = NULL;
DefElem *cache_value = NULL;
List *option;
if ( increment_by == (DefElem*) NULL ) /* INCREMENT BY */
new->increment_by = 1;
else if ( ( new->increment_by = get_param (increment_by) ) == 0 )
elog (WARN, "DefineSequence: can't INCREMENT by 0");
new->is_cycled = 'f';
foreach(option, seq->options)
{
DefElem *defel = (DefElem *) lfirst(option);
if ( max_value == (DefElem*) NULL ) /* MAXVALUE */
if ( new->increment_by > 0 )
new->max_value = SEQ_MAXVALUE; /* ascending seq */
if (!strcasecmp(defel->defname, "increment"))
increment_by = defel;
else if (!strcasecmp(defel->defname, "start"))
last_value = defel;
else if (!strcasecmp(defel->defname, "maxvalue"))
max_value = defel;
else if (!strcasecmp(defel->defname, "minvalue"))
min_value = defel;
else if (!strcasecmp(defel->defname, "cache"))
cache_value = defel;
else if (!strcasecmp(defel->defname, "cycle"))
{
if (defel->arg != (Node *) NULL)
elog(WARN, "DefineSequence: CYCLE ??");
new->is_cycled = 't';
}
else
elog(WARN, "DefineSequence: option \"%s\" not recognized",
defel->defname);
}
if (increment_by == (DefElem *) NULL) /* INCREMENT BY */
new->increment_by = 1;
else if ((new->increment_by = get_param(increment_by)) == 0)
elog(WARN, "DefineSequence: can't INCREMENT by 0");
if (max_value == (DefElem *) NULL) /* MAXVALUE */
if (new->increment_by > 0)
new->max_value = SEQ_MAXVALUE; /* ascending seq */
else
new->max_value = -1;/* descending seq */
else
new->max_value = -1; /* descending seq */
else
new->max_value = get_param (max_value);
new->max_value = get_param(max_value);
if ( min_value == (DefElem*) NULL ) /* MINVALUE */
if ( new->increment_by > 0 )
new->min_value = 1; /* ascending seq */
if (min_value == (DefElem *) NULL) /* MINVALUE */
if (new->increment_by > 0)
new->min_value = 1; /* ascending seq */
else
new->min_value = SEQ_MINVALUE; /* descending seq */
else
new->min_value = SEQ_MINVALUE; /* descending seq */
else
new->min_value = get_param (min_value);
if ( new->min_value >= new->max_value )
elog (WARN, "DefineSequence: MINVALUE (%d) can't be >= MAXVALUE (%d)",
new->min_value, new->max_value);
new->min_value = get_param(min_value);
if ( last_value == (DefElem*) NULL ) /* START WITH */
if ( new->increment_by > 0 )
new->last_value = new->min_value; /* ascending seq */
if (new->min_value >= new->max_value)
elog(WARN, "DefineSequence: MINVALUE (%d) can't be >= MAXVALUE (%d)",
new->min_value, new->max_value);
if (last_value == (DefElem *) NULL) /* START WITH */
if (new->increment_by > 0)
new->last_value = new->min_value; /* ascending seq */
else
new->last_value = new->max_value; /* descending seq */
else
new->last_value = new->max_value; /* descending seq */
else
new->last_value = get_param (last_value);
new->last_value = get_param(last_value);
if ( new->last_value < new->min_value )
elog (WARN, "DefineSequence: START value (%d) can't be < MINVALUE (%d)",
new->last_value, new->min_value);
if ( new->last_value > new->max_value )
elog (WARN, "DefineSequence: START value (%d) can't be > MAXVALUE (%d)",
new->last_value, new->max_value);
if (new->last_value < new->min_value)
elog(WARN, "DefineSequence: START value (%d) can't be < MINVALUE (%d)",
new->last_value, new->min_value);
if (new->last_value > new->max_value)
elog(WARN, "DefineSequence: START value (%d) can't be > MAXVALUE (%d)",
new->last_value, new->max_value);
if ( cache_value == (DefElem*) NULL ) /* CACHE */
new->cache_value = 1;
else if ( ( new->cache_value = get_param (cache_value) ) <= 0 )
elog (WARN, "DefineSequence: CACHE (%d) can't be <= 0",
new->cache_value);
if (cache_value == (DefElem *) NULL) /* CACHE */
new->cache_value = 1;
else if ((new->cache_value = get_param(cache_value)) <= 0)
elog(WARN, "DefineSequence: CACHE (%d) can't be <= 0",
new->cache_value);
}
static int
get_param (DefElem *def)
get_param(DefElem * def)
{
if ( def->arg == (Node*) NULL )
elog (WARN, "DefineSequence: \"%s\" value unspecified", def->defname);
if (def->arg == (Node *) NULL)
elog(WARN, "DefineSequence: \"%s\" value unspecified", def->defname);
if ( nodeTag (def->arg) == T_Integer )
return (intVal (def->arg));
elog (WARN, "DefineSequence: \"%s\" is to be integer", def->defname);
return (-1);
if (nodeTag(def->arg) == T_Integer)
return (intVal(def->arg));
elog(WARN, "DefineSequence: \"%s\" is to be integer", def->defname);
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--
* use rewrite rules to construct views
* use rewrite rules to construct views
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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 <postgres.h>
@ -42,69 +42,74 @@
*---------------------------------------------------------------------
*/
static void
DefineVirtualRelation(char *relname, List *tlist)
DefineVirtualRelation(char *relname, List * tlist)
{
CreateStmt createStmt;
List *attrList, *t;
TargetEntry *entry;
Resdom *res;
char *resname;
char *restypename;
/*
* create a list with one entry per attribute of this relation.
* Each entry is a two element list. The first element is the
* name of the attribute (a string) and the second the name of the type
* (NOTE: a string, not a type id!).
*/
attrList = NIL;
if (tlist!=NIL) {
foreach (t, tlist ) {
ColumnDef *def = makeNode(ColumnDef);
TypeName *typename;
CreateStmt createStmt;
List *attrList,
*t;
TargetEntry *entry;
Resdom *res;
char *resname;
char *restypename;
/*
* find the names of the attribute & its type
*/
entry = lfirst(t);
res = entry->resdom;
resname = res->resname;
restypename = tname(get_id_type(res->restype));
/*
* create a list with one entry per attribute of this relation. Each
* entry is a two element list. The first element is the name of the
* attribute (a string) and the second the name of the type (NOTE: a
* string, not a type id!).
*/
attrList = NIL;
if (tlist != NIL)
{
foreach(t, tlist)
{
ColumnDef *def = makeNode(ColumnDef);
TypeName *typename;
typename = makeNode(TypeName);
/*
* find the names of the attribute & its type
*/
entry = lfirst(t);
res = entry->resdom;
resname = res->resname;
restypename = tname(get_id_type(res->restype));
typename->name = pstrdup(restypename);
def->colname = pstrdup(resname);
typename = makeNode(TypeName);
def->typename = typename;
def->is_not_null = false;
def->defval = (char*) NULL;
typename->name = pstrdup(restypename);
def->colname = pstrdup(resname);
attrList = lappend(attrList, def);
def->typename = typename;
def->is_not_null = false;
def->defval = (char *) NULL;
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.
* All of them are nil...
*/
createStmt.relname = relname;
createStmt.tableElts = attrList;
/* createStmt.tableType = NULL;*/
createStmt.inhRelnames = NIL;
createStmt.archiveType = ARCH_NONE;
createStmt.location = -1;
createStmt.archiveLoc = -1;
createStmt.constraints = NIL;
/*
* finally create the relation...
*/
DefineRelation(&createStmt);
}
/*
* now create the parametesr for keys/inheritance etc. All of them are
* nil...
*/
createStmt.relname = relname;
createStmt.tableElts = attrList;
/* createStmt.tableType = NULL;*/
createStmt.inhRelnames = NIL;
createStmt.archiveType = ARCH_NONE;
createStmt.location = -1;
createStmt.archiveLoc = -1;
createStmt.constraints = NIL;
/*
* finally create the relation...
*/
DefineRelation(&createStmt);
}
/*------------------------------------------------------------------
* makeViewRetrieveRuleName
@ -118,84 +123,87 @@ DefineVirtualRelation(char *relname, List *tlist)
* XXX it also means viewName cannot be 16 chars long! - ay 11/94
*------------------------------------------------------------------
*/
char *
char *
MakeRetrieveViewRuleName(char *viewName)
{
/*
char buf[100];
char buf[100];
memset(buf, 0, sizeof(buf));
sprintf(buf, "_RET%.*s", NAMEDATALEN, viewName->data);
buf[15] = '\0';
namestrcpy(rule_name, buf);
memset(buf, 0, sizeof(buf));
sprintf(buf, "_RET%.*s", NAMEDATALEN, viewName->data);
buf[15] = '\0';
namestrcpy(rule_name, buf);
*/
char *buf;
buf = palloc(strlen(viewName) + 5);
sprintf(buf, "_RET%s",viewName);
return buf;
char *buf;
buf = palloc(strlen(viewName) + 5);
sprintf(buf, "_RET%s", viewName);
return buf;
}
static RuleStmt *
FormViewRetrieveRule(char *viewName, Query *viewParse)
FormViewRetrieveRule(char *viewName, Query * viewParse)
{
RuleStmt *rule;
char *rname;
Attr *attr;
/*
* Create a RuleStmt that corresponds to the suitable
* rewrite rule args for DefineQueryRewrite();
*/
rule = makeNode(RuleStmt);
rname = MakeRetrieveViewRuleName(viewName);
RuleStmt *rule;
char *rname;
Attr *attr;
attr = makeNode(Attr);
attr->relname = pstrdup(viewName);
/* attr->refname = pstrdup(viewName);*/
rule->rulename = pstrdup(rname);
rule->whereClause = NULL;
rule->event = CMD_SELECT;
rule->object = attr;
rule->instead = true;
rule->actions = lcons(viewParse, NIL);
return rule;
/*
* Create a RuleStmt that corresponds to the suitable rewrite rule
* args for DefineQueryRewrite();
*/
rule = makeNode(RuleStmt);
rname = MakeRetrieveViewRuleName(viewName);
attr = makeNode(Attr);
attr->relname = pstrdup(viewName);
/* attr->refname = pstrdup(viewName);*/
rule->rulename = pstrdup(rname);
rule->whereClause = NULL;
rule->event = CMD_SELECT;
rule->object = attr;
rule->instead = true;
rule->actions = lcons(viewParse, NIL);
return rule;
}
static void
DefineViewRules(char *viewName, Query *viewParse)
DefineViewRules(char *viewName, Query * viewParse)
{
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
replace_rule =
FormViewReplaceRule(viewName, viewParse);
append_rule =
FormViewAppendRule(viewName, viewParse);
delete_rule =
FormViewDeleteRule(viewName, viewParse);
#endif
DefineQueryRewrite(retrieve_rule);
RuleStmt *retrieve_rule = NULL;
#ifdef NOTYET
DefineQueryRewrite(replace_rule);
DefineQueryRewrite(append_rule);
DefineQueryRewrite(delete_rule);
RuleStmt *replace_rule = NULL;
RuleStmt *append_rule = NULL;
RuleStmt *delete_rule = NULL;
#endif
}
retrieve_rule =
FormViewRetrieveRule(viewName, viewParse);
#ifdef NOTYET
replace_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
}
/*---------------------------------------------------------------
* UpdateRangeTableOfViewParse
@ -216,88 +224,84 @@ DefineViewRules(char *viewName, Query *viewParse)
*---------------------------------------------------------------
*/
static void
UpdateRangeTableOfViewParse(char *viewName, Query *viewParse)
UpdateRangeTableOfViewParse(char *viewName, Query * viewParse)
{
List *old_rt;
List *new_rt;
RangeTblEntry *rt_entry1, *rt_entry2;
/*
* first offset all var nodes by 2
*/
OffsetVarNodes((Node*)viewParse->targetList, 2);
OffsetVarNodes(viewParse->qual, 2);
/*
* find the old range table...
*/
old_rt = viewParse->rtable;
List *old_rt;
List *new_rt;
RangeTblEntry *rt_entry1,
*rt_entry2;
/*
* create the 2 new range table entries and form the new
* range table...
* CURRENT first, then NEW....
*/
rt_entry1 =
addRangeTableEntry(NULL, (char*)viewName, "*CURRENT*",
FALSE, FALSE, NULL);
rt_entry2 =
addRangeTableEntry(NULL, (char*)viewName, "*NEW*",
FALSE, FALSE, NULL);
new_rt = lcons(rt_entry2, old_rt);
new_rt = lcons(rt_entry1, new_rt);
/*
* Now the tricky part....
* Update the range table in place... Be careful here, or
* hell breaks loooooooooooooOOOOOOOOOOOOOOOOOOSE!
*/
viewParse->rtable = new_rt;
/*
* first offset all var nodes by 2
*/
OffsetVarNodes((Node *) viewParse->targetList, 2);
OffsetVarNodes(viewParse->qual, 2);
/*
* find the old range table...
*/
old_rt = viewParse->rtable;
/*
* create the 2 new range table entries and form the new range
* table... CURRENT first, then NEW....
*/
rt_entry1 =
addRangeTableEntry(NULL, (char *) viewName, "*CURRENT*",
FALSE, FALSE, NULL);
rt_entry2 =
addRangeTableEntry(NULL, (char *) viewName, "*NEW*",
FALSE, FALSE, NULL);
new_rt = lcons(rt_entry2, old_rt);
new_rt = lcons(rt_entry1, new_rt);
/*
* Now the tricky part.... Update the range table in place... Be
* careful here, or hell breaks loooooooooooooOOOOOOOOOOOOOOOOOOSE!
*/
viewParse->rtable = new_rt;
}
/*-------------------------------------------------------------------
* DefineView
*
* - takes a "viewname", "parsetree" pair and then
* 1) construct the "virtual" relation
* 2) commit the command but NOT the transaction,
* so that the relation exists
* before the rules are defined.
* 2) define the "n" rules specified in the PRS2 paper
* over the "virtual" relation
* - takes a "viewname", "parsetree" pair and then
* 1) construct the "virtual" relation
* 2) commit the command but NOT the transaction,
* so that the relation exists
* before the rules are defined.
* 2) define the "n" rules specified in the PRS2 paper
* over the "virtual" relation
*-------------------------------------------------------------------
*/
void
DefineView(char *viewName, Query *viewParse)
DefineView(char *viewName, Query * viewParse)
{
List *viewTlist;
List *viewTlist;
viewTlist = viewParse->targetList;
/*
* Create the "view" relation
* NOTE: if it already exists, the xaxt will be aborted.
*/
DefineVirtualRelation(viewName, viewTlist);
/*
* The relation we have just created is not visible
* to any other commands running with the same transaction &
* command id.
* So, increment the command id counter (but do NOT pfree any
* memory!!!!)
*/
CommandCounterIncrement();
/*
* The range table of 'viewParse' does not contain entries
* for the "CURRENT" and "NEW" relations.
* So... add them!
* NOTE: we make the update in place! After this call 'viewParse'
* will never be what it used to be...
*/
UpdateRangeTableOfViewParse(viewName, viewParse);
DefineViewRules(viewName, viewParse);
viewTlist = viewParse->targetList;
/*
* Create the "view" relation NOTE: if it already exists, the xaxt
* will be aborted.
*/
DefineVirtualRelation(viewName, viewTlist);
/*
* The relation we have just created is not visible to any other
* commands running with the same transaction & command id. So,
* increment the command id counter (but do NOT pfree any memory!!!!)
*/
CommandCounterIncrement();
/*
* The range table of 'viewParse' does not contain entries for the
* "CURRENT" and "NEW" relations. So... add them! NOTE: we make the
* update in place! After this call 'viewParse' will never be what it
* used to be...
*/
UpdateRangeTableOfViewParse(viewName, viewParse);
DefineViewRules(viewName, viewParse);
}
/*------------------------------------------------------------------
@ -309,23 +313,22 @@ DefineView(char *viewName, Query *viewParse)
void
RemoveView(char *viewName)
{
char* rname;
/*
* first remove all the "view" rules...
* Currently we only have one!
*/
rname = MakeRetrieveViewRuleName(viewName);
RemoveRewriteRule(rname);
char *rname;
/*
* we don't really need that, but just in case...
*/
CommandCounterIncrement();
/*
* now remove the relation.
*/
heap_destroy(viewName);
pfree(rname);
/*
* first remove all the "view" rules... Currently we only have one!
*/
rname = MakeRetrieveViewRuleName(viewName);
RemoveRewriteRule(rname);
/*
* we don't really need that, but just in case...
*/
CommandCounterIncrement();
/*
* now remove the relation.
*/
heap_destroy(viewName);
pfree(rname);
}

View File

@ -1,32 +1,32 @@
/*-------------------------------------------------------------------------
*
* execAmi.c--
* miscellanious executor access method routines
* miscellanious executor access method routines
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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
* ExecBeginScan \ / ambeginscan
* ExecCloseR \ / amclose
* ExecInsert \ executor interface / aminsert
* ExecReScanNode / to access methods \ amrescan
* ExecReScanR / \ amrescan
* ExecMarkPos / \ ammarkpos
* ExecRestrPos / \ amrestpos
* ExecOpenScanR \ / amopen
* ExecBeginScan \ / ambeginscan
* ExecCloseR \ / amclose
* ExecInsert \ executor interface / aminsert
* ExecReScanNode / to access methods \ amrescan
* ExecReScanR / \ amrescan
* ExecMarkPos / \ ammarkpos
* 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"
@ -43,409 +43,430 @@
#include "access/heapam.h"
#include "catalog/heap.h"
static Pointer ExecBeginScan(Relation relation, int nkeys, ScanKey skeys,
bool isindex, ScanDirection dir, TimeQual time_range);
static Pointer
ExecBeginScan(Relation relation, int nkeys, ScanKey skeys,
bool isindex, ScanDirection dir, TimeQual time_range);
static Relation ExecOpenR(Oid relationOid, bool isindex);
/* ----------------------------------------------------------------
* ExecOpenScanR
* ExecOpenScanR
*
* old comments:
* Parameters:
* relation -- relation to be opened and scanned.
* nkeys -- number of keys
* skeys -- keys to restrict scanning
* isindex -- if this is true, the relation is the relid of
* an index relation, else it is an index into the
* range table.
* Returns the relation as(relDesc scanDesc)
* If this structure is changed, need to modify the access macros
* defined in execInt.h.
* Parameters:
* relation -- relation to be opened and scanned.
* nkeys -- number of keys
* skeys -- keys to restrict scanning
* isindex -- if this is true, the relation is the relid of
* an index relation, else it is an index into the
* range table.
* Returns the relation as(relDesc scanDesc)
* If this structure is changed, need to modify the access macros
* defined in execInt.h.
* ----------------------------------------------------------------
*/
void
ExecOpenScanR(Oid relOid,
int nkeys,
ScanKey skeys,
bool isindex,
ScanDirection dir,
TimeQual timeRange,
Relation *returnRelation, /* return */
Pointer *returnScanDesc) /* return */
int nkeys,
ScanKey skeys,
bool isindex,
ScanDirection dir,
TimeQual timeRange,
Relation * returnRelation, /* return */
Pointer * returnScanDesc) /* return */
{
Relation relation;
Pointer scanDesc;
/* ----------------
* note: scanDesc returned by ExecBeginScan can be either
* a HeapScanDesc or an IndexScanDesc so for now we
* make it a Pointer. There should be a better scan
* abstraction someday -cim 9/9/89
* ----------------
*/
relation = ExecOpenR(relOid, isindex);
scanDesc = ExecBeginScan(relation,
nkeys,
skeys,
isindex,
dir,
timeRange);
if (returnRelation != NULL)
*returnRelation = relation;
if (scanDesc != NULL)
*returnScanDesc = scanDesc;
Relation relation;
Pointer scanDesc;
/* ----------------
* note: scanDesc returned by ExecBeginScan can be either
* a HeapScanDesc or an IndexScanDesc so for now we
* make it a Pointer. There should be a better scan
* abstraction someday -cim 9/9/89
* ----------------
*/
relation = ExecOpenR(relOid, isindex);
scanDesc = ExecBeginScan(relation,
nkeys,
skeys,
isindex,
dir,
timeRange);
if (returnRelation != NULL)
*returnRelation = relation;
if (scanDesc != NULL)
*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)
{
Relation relation;
relation = (Relation) NULL;
Relation relation;
/* ----------------
* 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.");
relation = (Relation) NULL;
return relation;
/* ----------------
* 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.");
return relation;
}
/* ----------------------------------------------------------------
* ExecBeginScan
* ExecBeginScan
*
* beginscans a relation in current direction.
* beginscans a relation in current direction.
*
* XXX fix parameters to AMbeginscan (and btbeginscan)
* currently we need to pass a flag stating whether
* or not the scan should begin at an endpoint of
* the relation.. Right now we always pass false
* -cim 9/14/89
* XXX fix parameters to AMbeginscan (and btbeginscan)
* currently we need to pass a flag stating whether
* or not the scan should begin at an endpoint of
* the relation.. Right now we always pass false
* -cim 9/14/89
* ----------------------------------------------------------------
*/
static Pointer
static Pointer
ExecBeginScan(Relation relation,
int nkeys,
ScanKey skeys,
bool isindex,
ScanDirection dir,
TimeQual time_range)
int nkeys,
ScanKey skeys,
bool isindex,
ScanDirection dir,
TimeQual time_range)
{
Pointer scanDesc;
scanDesc = NULL;
Pointer scanDesc;
/* ----------------
* open the appropriate type of scan.
*
* Note: ambeginscan()'s second arg is a boolean indicating
* that the scan should be done in reverse.. That is,
* if you pass it true, then the scan is backward.
* ----------------
*/
if (isindex) {
scanDesc = (Pointer) index_beginscan(relation,
false, /* see above comment */
nkeys,
skeys);
} else {
scanDesc = (Pointer) heap_beginscan(relation,
ScanDirectionIsBackward(dir),
time_range,
nkeys,
skeys);
}
if (scanDesc == NULL)
elog(DEBUG, "ExecBeginScan: scanDesc = NULL, heap_beginscan failed.");
return scanDesc;
scanDesc = NULL;
/* ----------------
* open the appropriate type of scan.
*
* Note: ambeginscan()'s second arg is a boolean indicating
* that the scan should be done in reverse.. That is,
* if you pass it true, then the scan is backward.
* ----------------
*/
if (isindex)
{
scanDesc = (Pointer) index_beginscan(relation,
false, /* see above comment */
nkeys,
skeys);
}
else
{
scanDesc = (Pointer) heap_beginscan(relation,
ScanDirectionIsBackward(dir),
time_range,
nkeys,
skeys);
}
if (scanDesc == NULL)
elog(DEBUG, "ExecBeginScan: scanDesc = NULL, heap_beginscan failed.");
return scanDesc;
}
/* ----------------------------------------------------------------
* ExecCloseR
* ExecCloseR
*
* closes the relation and scan descriptor for a scan or sort
* node. Also closes index relations and scans for index scans.
* closes the relation and scan descriptor for a scan or sort
* node. Also closes index relations and scans for index scans.
*
* old comments
* closes the relation indicated in 'relID'
* closes the relation indicated in 'relID'
* ----------------------------------------------------------------
*/
void
ExecCloseR(Plan *node)
ExecCloseR(Plan * node)
{
CommonScanState *state;
Relation relation;
HeapScanDesc scanDesc;
/* ----------------
* shut down the heap scan and close the heap relation
* ----------------
*/
switch (nodeTag(node)) {
case T_SeqScan:
state = ((SeqScan *)node)->scanstate;
break;
CommonScanState *state;
Relation relation;
HeapScanDesc scanDesc;
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.
/* ----------------
* shut down the heap scan and close the heap relation
* ----------------
*/
return;
switch (nodeTag(node))
{
case T_Tee:
ExecTeeReScan((Tee*) node, exprCtxt, parent);
break;
case T_SeqScan:
state = ((SeqScan *) node)->scanstate;
break;
default:
elog(WARN, "ExecReScan: not a seqscan or indexscan node.");
return;
}
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]);
}
}
}
/* ----------------------------------------------------------------
* 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
ExecReScanR(Relation relDesc, /* LLL relDesc unused */
HeapScanDesc scanDesc,
ScanDirection direction,
int nkeys, /* LLL nkeys unused */
ScanKey skeys)
ExecReScanR(Relation relDesc, /* LLL relDesc unused */
HeapScanDesc scanDesc,
ScanDirection direction,
int nkeys, /* LLL nkeys unused */
ScanKey skeys)
{
if (scanDesc != NULL)
heap_rescan(scanDesc, /* scan desc */
ScanDirectionIsBackward(direction), /* backward flag */
skeys); /* scan keys */
return scanDesc;
if (scanDesc != NULL)
heap_rescan(scanDesc, /* scan desc */
ScanDirectionIsBackward(direction), /* backward flag */
skeys); /* scan keys */
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
ExecMarkPos(Plan *node)
ExecMarkPos(Plan * node)
{
switch(nodeTag(node)) {
case T_SeqScan:
ExecSeqMarkPos((SeqScan *) node);
break;
switch (nodeTag(node))
{
case T_SeqScan:
ExecSeqMarkPos((SeqScan *) node);
break;
case T_IndexScan:
ExecIndexMarkPos((IndexScan *) node);
break;
case T_IndexScan:
ExecIndexMarkPos((IndexScan *) node);
break;
case T_Sort:
ExecSortMarkPos((Sort *) node);
break;
case T_Sort:
ExecSortMarkPos((Sort *) node);
break;
default:
/* elog(DEBUG, "ExecMarkPos: unsupported node type"); */
break;
}
return;
default:
/* elog(DEBUG, "ExecMarkPos: unsupported node type"); */
break;
}
return;
}
/* ----------------------------------------------------------------
* ExecRestrPos
* ExecRestrPos
*
* restores the scan position previously saved with ExecMarkPos()
* restores the scan position previously saved with ExecMarkPos()
* ----------------------------------------------------------------
*/
void
ExecRestrPos(Plan *node)
ExecRestrPos(Plan * node)
{
switch(nodeTag(node)) {
case T_SeqScan:
ExecSeqRestrPos((SeqScan *) node);
return;
case T_IndexScan:
ExecIndexRestrPos((IndexScan *) node);
return;
case T_Sort:
ExecSortRestrPos((Sort *) node);
return;
switch (nodeTag(node))
{
case T_SeqScan:
ExecSeqRestrPos((SeqScan *) node);
return;
default:
/* elog(DEBUG, "ExecRestrPos: node type not supported"); */
return;
}
case T_IndexScan:
ExecIndexRestrPos((IndexScan *) node);
return;
case T_Sort:
ExecSortRestrPos((Sort *) node);
return;
default:
/* elog(DEBUG, "ExecRestrPos: node type not supported"); */
return;
}
}
/* ----------------------------------------------------------------
* ExecCreatR
* ExecCreatR
*
* old comments
* Creates a relation.
* Creates a relation.
*
* Parameters:
* attrType -- type information on the attributes.
* accessMtd -- access methods used to access the created relation.
* relation -- optional. Either an index to the range table or
* negative number indicating a temporary relation.
* A temporary relation is assume is this field is absent.
* Parameters:
* attrType -- type information on the attributes.
* accessMtd -- access methods used to access the created relation.
* relation -- optional. Either an index to the range table or
* negative number indicating a temporary relation.
* A temporary relation is assume is this field is absent.
* ----------------------------------------------------------------
*/
Relation
ExecCreatR(TupleDesc tupType,
Oid relationOid)
Oid relationOid)
{
Relation relDesc;
Relation relDesc;
EU3_printf("ExecCreatR: %s type=%d oid=%d\n",
"entering: ", tupType, relationOid);
CXT1_printf("ExecCreatR: context is %d\n", CurrentMemoryContext);
relDesc = NULL;
if (relationOid == _TEMP_RELATION_ID_ ) {
/* ----------------
* create a temporary relation
* (currently the planner always puts a _TEMP_RELATION_ID
* in the relation argument so we expect this to be the case although
* it's possible that someday we'll get the name from
* from the range table.. -cim 10/12/89)
* ----------------
*/
EU3_printf("ExecCreatR: %s type=%d oid=%d\n",
"entering: ", tupType, relationOid);
CXT1_printf("ExecCreatR: context is %d\n", CurrentMemoryContext);
relDesc = NULL;
if (relationOid == _TEMP_RELATION_ID_)
{
/* ----------------
* create a temporary relation
* (currently the planner always puts a _TEMP_RELATION_ID
* in the relation argument so we expect this to be the case although
* 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++);
EU1_printf("ExecCreatR: attempting to create %s\n", tempname);
sprintf(tempname, "temp_%d.%d", getpid(), tmpcnt++);
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.");
EU1_printf("ExecCreatR: returning relDesc=%d\n", relDesc);
return relDesc;
/*
* 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.");
EU1_printf("ExecCreatR: returning relDesc=%d\n", relDesc);
return relDesc;
}

View File

@ -1,29 +1,29 @@
/*-------------------------------------------------------------------------
*
* execFlatten.c--
* This file handles the nodes associated with flattening sets in the
* target list of queries containing functions returning sets.
* This file handles the nodes associated with flattening sets in the
* target list of queries containing functions returning sets.
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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() -
* Iterate through the all return tuples/base types from a function one
* at time (i.e. one per ExecEvalIter call). Not really needed for
* postquel functions, but for reasons of orthogonality, these nodes
* exist above pq functions as well as c functions.
* Iterate through the all return tuples/base types from a function one
* at time (i.e. one per ExecEvalIter call). Not really needed for
* postquel functions, but for reasons of orthogonality, these nodes
* exist above pq functions as well as c functions.
*
* ExecEvalFjoin() -
* 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
* node does the actual flattening work.
* 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
* node does the actual flattening work.
*/
#include "postgres.h"
#include "nodes/primnodes.h"
@ -33,208 +33,216 @@
#include "executor/execFlatten.h"
#ifdef SETS_FIXED
static bool FjoinBumpOuterNodes(TargetEntry *tlist, ExprContext *econtext,
DatumPtr results, char *nulls);
static bool
FjoinBumpOuterNodes(TargetEntry * tlist, ExprContext * econtext,
DatumPtr results, char *nulls);
#endif
Datum
ExecEvalIter(Iter *iterNode,
ExprContext *econtext,
bool *resultIsNull,
bool *iterIsDone)
ExecEvalIter(Iter * iterNode,
ExprContext * econtext,
bool * resultIsNull,
bool * iterIsDone)
{
Node *expression;
expression = iterNode->iterexpr;
/*
* Really Iter nodes are only needed for C functions, postquel function
* by their nature return 1 result at a time. For now we are only worrying
* about postquel functions, c functions will come later.
*/
return ExecEvalExpr(expression, econtext, resultIsNull, iterIsDone);
Node *expression;
expression = iterNode->iterexpr;
/*
* Really Iter nodes are only needed for C functions, postquel
* function by their nature return 1 result at a time. For now we are
* only worrying about postquel functions, c functions will come
* later.
*/
return ExecEvalExpr(expression, econtext, resultIsNull, iterIsDone);
}
void
ExecEvalFjoin(TargetEntry *tlist,
ExprContext *econtext,
bool *isNullVect,
bool *fj_isDone)
ExecEvalFjoin(TargetEntry * tlist,
ExprContext * econtext,
bool * isNullVect,
bool * fj_isDone)
{
#ifdef SETS_FIXED
bool isDone;
int curNode;
List *tlistP;
bool isDone;
int curNode;
List *tlistP;
Fjoin *fjNode = tlist->fjoin;
DatumPtr resVect = fjNode->fj_results;
BoolPtr alwaysDone = fjNode->fj_alwaysDone;
if (fj_isDone) *fj_isDone = false;
/*
* For the next tuple produced by the plan, we need to re-initialize
* the Fjoin node.
*/
if (!fjNode->fj_initialized)
Fjoin *fjNode = tlist->fjoin;
DatumPtr resVect = fjNode->fj_results;
BoolPtr alwaysDone = fjNode->fj_alwaysDone;
if (fj_isDone)
*fj_isDone = false;
/*
* For the next tuple produced by the plan, we need to re-initialize
* the Fjoin node.
*/
if (!fjNode->fj_initialized)
{
/*
* Initialize all of the Outer nodes
*/
curNode = 1;
foreach(tlistP, lnext(tlist))
/*
* Initialize all of the Outer nodes
*/
curNode = 1;
foreach(tlistP, lnext(tlist))
{
TargetEntry *tle = lfirst(tlistP);
resVect[curNode] = ExecEvalIter((Iter*)tle->expr,
econtext,
&isNullVect[curNode],
&isDone);
if (isDone)
isNullVect[curNode] = alwaysDone[curNode] = true;
else
alwaysDone[curNode] = false;
curNode++;
TargetEntry *tle = lfirst(tlistP);
resVect[curNode] = ExecEvalIter((Iter *) tle->expr,
econtext,
&isNullVect[curNode],
&isDone);
if (isDone)
isNullVect[curNode] = alwaysDone[curNode] = true;
else
alwaysDone[curNode] = false;
curNode++;
}
/*
* Initialize the inner node
*/
resVect[0] = ExecEvalIter((Iter*)fjNode->fj_innerNode->expr,
econtext,
&isNullVect[0],
&isDone);
if (isDone)
isNullVect[0] = alwaysDone[0] = true;
else
alwaysDone[0] = false;
/*
* Mark the Fjoin as initialized now.
*/
fjNode->fj_initialized = TRUE;
/*
* If the inner node is always done, then we are done for now
*/
if (isDone)
return;
/*
* Initialize the inner node
*/
resVect[0] = ExecEvalIter((Iter *) fjNode->fj_innerNode->expr,
econtext,
&isNullVect[0],
&isDone);
if (isDone)
isNullVect[0] = alwaysDone[0] = true;
else
alwaysDone[0] = false;
/*
* Mark the Fjoin as initialized now.
*/
fjNode->fj_initialized = TRUE;
/*
* If the inner node is always done, then we are done for now
*/
if (isDone)
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.
*/
for (curNode=fjNode->fj_nNodes-1; curNode >= 0; curNode--)
isNullVect[curNode] = alwaysDone[curNode];
if (alwaysDone[0] == true)
/*
* 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.
*/
for (curNode = fjNode->fj_nNodes - 1; curNode >= 0; curNode--)
isNullVect[curNode] = alwaysDone[curNode];
if (alwaysDone[0] == true)
{
*fj_isDone = FjoinBumpOuterNodes(tlist,
econtext,
resVect,
isNullVect);
return;
*fj_isDone = FjoinBumpOuterNodes(tlist,
econtext,
resVect,
isNullVect);
return;
}
else
resVect[0] = ExecEvalIter((Iter*)fjNode->fj_innerNode->expr,
econtext,
&isNullVect[0],
&isDone);
else
resVect[0] = ExecEvalIter((Iter *) fjNode->fj_innerNode->expr,
econtext,
&isNullVect[0],
&isDone);
}
/*
* if the inner node is done
*/
if (isDone)
/*
* if the inner node is done
*/
if (isDone)
{
*fj_isDone = FjoinBumpOuterNodes(tlist,
econtext,
resVect,
isNullVect);
if (*fj_isDone)
return;
resVect[0] = ExecEvalIter((Iter*)fjNode->fj_innerNode->expr,
econtext,
&isNullVect[0],
&isDone);
*fj_isDone = FjoinBumpOuterNodes(tlist,
econtext,
resVect,
isNullVect);
if (*fj_isDone)
return;
resVect[0] = ExecEvalIter((Iter *) fjNode->fj_innerNode->expr,
econtext,
&isNullVect[0],
&isDone);
}
#endif
return;
return;
}
#ifdef SETS_FIXED
static bool
FjoinBumpOuterNodes(TargetEntry *tlist,
ExprContext *econtext,
DatumPtr results,
char *nulls)
static bool
FjoinBumpOuterNodes(TargetEntry * tlist,
ExprContext * econtext,
DatumPtr results,
char *nulls)
{
bool funcIsDone = true;
Fjoin *fjNode = tlist->fjoin;
char *alwaysDone = fjNode->fj_alwaysDone;
List *outerList = lnext(tlist);
List *trailers = lnext(tlist);
int trailNode = 1;
int curNode = 1;
/*
* 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.
*/
while ((funcIsDone == true) && (outerList != NIL))
bool funcIsDone = true;
Fjoin *fjNode = tlist->fjoin;
char *alwaysDone = fjNode->fj_alwaysDone;
List *outerList = lnext(tlist);
List *trailers = lnext(tlist);
int trailNode = 1;
int curNode = 1;
/*
* 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.
*/
while ((funcIsDone == true) && (outerList != NIL))
{
TargetEntry *tle = lfirst(outerList);
if (alwaysDone[curNode] == true)
nulls[curNode] = 'n';
else
results[curNode] = ExecEvalIter((Iter)tle->expr,
econtext,
&nulls[curNode],
&funcIsDone);
curNode++;
outerList = lnext(outerList);
TargetEntry *tle = lfirst(outerList);
if (alwaysDone[curNode] == true)
nulls[curNode] = 'n';
else
results[curNode] = ExecEvalIter((Iter) tle->expr,
econtext,
&nulls[curNode],
&funcIsDone);
curNode++;
outerList = lnext(outerList);
}
/*
* If every function is done, then we are done flattening.
* Mark the Fjoin node unitialized, it is time to get the
* next tuple from the plan and redo all of the flattening.
*/
if (funcIsDone)
/*
* If every function is done, then we are done flattening. Mark the
* Fjoin node unitialized, it is time to get the next tuple from the
* plan and redo all of the flattening.
*/
if (funcIsDone)
{
set_fj_initialized(fjNode, false);
return (true);
set_fj_initialized(fjNode, false);
return (true);
}
/*
* 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.
*/
trailNode = 1;
while (trailNode != curNode-1)
/*
* 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.
*/
trailNode = 1;
while (trailNode != curNode - 1)
{
TargetEntry *tle = lfirst(trailers);
if (alwaysDone[trailNode] != true)
results[trailNode] = ExecEvalIter((Iter)tle->expr,
econtext,
&nulls[trailNode],
&funcIsDone);
trailNode++;
trailers = lnext(trailers);
TargetEntry *tle = lfirst(trailers);
if (alwaysDone[trailNode] != true)
results[trailNode] = ExecEvalIter((Iter) tle->expr,
econtext,
&nulls[trailNode],
&funcIsDone);
trailNode++;
trailers = lnext(trailers);
}
return false;
return false;
}
#endif

View File

@ -1,13 +1,13 @@
/*-------------------------------------------------------------------------
*
* junk.c--
* Junk attribute support stuff....
* Junk attribute support stuff....
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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,37 +20,37 @@
#include "access/heapam.h"
#include "executor/executor.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
* of ExecProject() and the ProjectionInfo node.
* -cim 6/3/91
*
* XXX this stuff should be rewritten to take advantage
* of ExecProject() and the ProjectionInfo node.
* -cim 6/3/91
*
* An attribute of a tuple living inside the executor, can be
* either a normal attribute or a "junk" attribute. "junk" attributes
* never make it out of the executor, i.e. they are never printed,
* returned or stored in disk. Their only purpose in life is to
* store some information useful only to the executor, mainly the values
* of some system attributes like "ctid" or rule locks.
*
*
* The general idea is the following: A target list consists of a list of
* Resdom nodes & expression pairs. Each Resdom node has an attribute
* called 'resjunk'. If the value of this attribute is 1 then the
* corresponding attribute is a "junk" attribute.
*
*
* When we initialize a plan we call 'ExecInitJunkFilter' to create
* and store the appropriate information in the 'es_junkFilter' attribute of
* EState.
*
*
* We then execute the plan ignoring the "resjunk" attributes.
*
*
* Finally, when at the top level we get back a tuple, we can call
* 'ExecGetJunkAttribute' to retrieve the value of the junk attributes we
* are interested in, and 'ExecRemoveJunk' to remove all the junk attributes
* from a tuple. This new "clean" tuple is then printed, replaced, deleted
* or inserted.
*
*
*-------------------------------------------------------------------------
*/
@ -60,174 +60,196 @@
* Initialize the Junk filter.
*-------------------------------------------------------------------------
*/
JunkFilter *
ExecInitJunkFilter(List *targetList)
JunkFilter *
ExecInitJunkFilter(List * targetList)
{
JunkFilter *junkfilter;
List *cleanTargetList;
int len, cleanLength;
TupleDesc tupType, cleanTupType;
List *t;
TargetEntry *tle;
Resdom *resdom, *cleanResdom;
int resjunk;
AttrNumber cleanResno;
AttrNumber *cleanMap;
Size size;
Node *expr;
JunkFilter *junkfilter;
List *cleanTargetList;
int len,
cleanLength;
TupleDesc tupType,
cleanTupType;
List *t;
TargetEntry *tle;
Resdom *resdom,
*cleanResdom;
int resjunk;
AttrNumber cleanResno;
AttrNumber *cleanMap;
Size size;
Node *expr;
/* ---------------------
* First find the "clean" target list, i.e. all the entries
* in the original target list which have a zero 'resjunk'
* NOTE: make copy of the Resdom nodes, because we have
* to change the 'resno's...
* ---------------------
*/
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);
/* ---------------------
* First find the "clean" target list, i.e. all the entries
* in the original target list which have a zero 'resjunk'
* NOTE: make copy of the Resdom nodes, because we have
* to change the 'resno's...
* ---------------------
*/
cleanTargetList = NIL;
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 ++;
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 {
else
{
#ifdef SETS_FIXED
List fjListP;
List fjList = lfirst(t);
Fjoin fjNode = (Fjoin)lfirst(fjList);
List *fjListP;
Fjoin *cleanFjoin;
List *cleanFjList;
List *fjList = lfirst(t);
Fjoin *fjNode = (Fjoin *) tl_node(fjList);
/* what the hell is this????? */
resdom = (Resdom) lfirst(get_fj_innerNode(fjNode));
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
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);
/* ---------------------
* 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;
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
ExecGetJunkAttribute(JunkFilter *junkfilter,
TupleTableSlot *slot,
char *attrName,
Datum *value,
bool *isNull)
ExecGetJunkAttribute(JunkFilter * junkfilter,
TupleTableSlot * slot,
char *attrName,
Datum * value,
bool * isNull)
{
List *targetList;
List *t;
Resdom *resdom;
AttrNumber resno;
char *resname;
int resjunk;
TupleDesc tupType;
HeapTuple tuple;
/* ---------------------
* first look in the junkfilter's target list for
* an attribute with the given name
* ---------------------
*/
resno = InvalidAttrNumber;
targetList = junkfilter->jf_targetList;
foreach (t, targetList) {
TargetEntry *tle = lfirst(t);
resdom = tle->resdom;
resname = resdom->resname;
resjunk = resdom->resjunk;
if (resjunk != 0 && (strcmp(resname, attrName) == 0)) {
/* We found it ! */
resno = resdom->resno;
break;
List *targetList;
List *t;
Resdom *resdom;
AttrNumber resno;
char *resname;
int resjunk;
TupleDesc tupType;
HeapTuple tuple;
/* ---------------------
* first look in the junkfilter's target list for
* an attribute with the given name
* ---------------------
*/
resno = InvalidAttrNumber;
targetList = junkfilter->jf_targetList;
foreach(t, targetList)
{
TargetEntry *tle = lfirst(t);
resdom = tle->resdom;
resname = resdom->resname;
resjunk = resdom->resjunk;
if (resjunk != 0 && (strcmp(resname, attrName) == 0))
{
/* We found it ! */
resno = resdom->resno;
break;
}
}
}
if (resno == InvalidAttrNumber) {
/* Ooops! We couldn't find this attribute... */
return(false);
}
/* ---------------------
* Now extract the attribute value from the tuple.
* ---------------------
*/
tuple = slot->val;
tupType = (TupleDesc) junkfilter->jf_tupType;
*value = (Datum)
heap_getattr(tuple, InvalidBuffer, resno, tupType, isNull);
return true;
if (resno == InvalidAttrNumber)
{
/* Ooops! We couldn't find this attribute... */
return (false);
}
/* ---------------------
* Now extract the attribute value from the tuple.
* ---------------------
*/
tuple = slot->val;
tupType = (TupleDesc) junkfilter->jf_tupType;
*value = (Datum)
heap_getattr(tuple, InvalidBuffer, resno, tupType, isNull);
return true;
}
/*-------------------------------------------------------------------------
@ -302,94 +328,98 @@ ExecGetJunkAttribute(JunkFilter *junkfilter,
*-------------------------------------------------------------------------
*/
HeapTuple
ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
ExecRemoveJunk(JunkFilter * junkfilter, TupleTableSlot * slot)
{
HeapTuple tuple;
HeapTuple cleanTuple;
AttrNumber *cleanMap;
TupleDesc cleanTupType;
TupleDesc tupType;
int cleanLength;
bool isNull;
int i;
Size size;
Datum *values;
char *nulls;
Datum values_array[64];
char nulls_array[64];
/* ----------------
* get info from the slot and the junk filter
* ----------------
*/
tuple = slot->val;
tupType = (TupleDesc) junkfilter->jf_tupType;
cleanTupType = (TupleDesc) junkfilter->jf_cleanTupType;
cleanLength = junkfilter->jf_cleanLength;
cleanMap = junkfilter->jf_cleanMap;
/* ---------------------
* Handle the trivial case first.
* ---------------------
*/
if (cleanLength == 0)
return (HeapTuple) NULL;
/* ---------------------
* Create the arrays that will hold the attribute values
* and the null information for the new "clean" tuple.
*
* Note: we use memory on the stack to optimize things when
* we are dealing with a small number of tuples.
* for large tuples we just use palloc.
* ---------------------
*/
if (cleanLength > 64) {
size = cleanLength * sizeof(Datum);
values = (Datum *) palloc(size);
size = cleanLength * sizeof(char);
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
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);
}
HeapTuple tuple;
HeapTuple cleanTuple;
AttrNumber *cleanMap;
TupleDesc cleanTupType;
TupleDesc tupType;
int cleanLength;
bool isNull;
int i;
Size size;
Datum *values;
char *nulls;
Datum values_array[64];
char nulls_array[64];
/* ----------------
* get info from the slot and the junk filter
* ----------------
*/
tuple = slot->val;
tupType = (TupleDesc) junkfilter->jf_tupType;
cleanTupType = (TupleDesc) junkfilter->jf_cleanTupType;
cleanLength = junkfilter->jf_cleanLength;
cleanMap = junkfilter->jf_cleanMap;
/* ---------------------
* Handle the trivial case first.
* ---------------------
*/
if (cleanLength == 0)
return (HeapTuple) NULL;
/* ---------------------
* Create the arrays that will hold the attribute values
* and the null information for the new "clean" tuple.
*
* Note: we use memory on the stack to optimize things when
* we are dealing with a small number of tuples.
* for large tuples we just use palloc.
* ---------------------
*/
if (cleanLength > 64)
{
size = cleanLength * sizeof(Datum);
values = (Datum *) palloc(size);
size = cleanLength * sizeof(char);
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
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--
* contains dispatch functions which call the appropriate "initialize",
* "get a tuple", and "cleanup" routines for the given node type.
* If the node has children, then it will presumably call ExecInitNode,
* ExecProcNode, or ExecEndNode on it's subnodes and do the appropriate
* processing..
* contains dispatch functions which call the appropriate "initialize",
* "get a tuple", and "cleanup" routines for the given node type.
* If the node has children, then it will presumably call ExecInitNode,
* ExecProcNode, or ExecEndNode on it's subnodes and do the appropriate
* processing..
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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
* ExecInitNode - initialize a plan node and it's subplans
* ExecProcNode - get a tuple by executing the plan node
* ExecEndNode - shut down a plan node and it's subplans
* INTERFACE ROUTINES
* ExecInitNode - initialize a plan node and it's subplans
* ExecProcNode - get a tuple by executing the plan node
* ExecEndNode - shut down a plan node and it's subplans
*
* NOTES
* This used to be three files. It is now all combined into
* one file so that it is easier to keep ExecInitNode, ExecProcNode,
* and ExecEndNode in sync when new nodes are added.
*
* EXAMPLE
* 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:
* NOTES
* This used to be three files. It is now all combined into
* one file so that it is easier to keep ExecInitNode, ExecProcNode,
* and ExecEndNode in sync when new nodes are added.
*
* retrieve (DEPT.no_emps, EMP.age)
* where EMP.name = DEPT.mgr and
* DEPT.name = "shoe"
*
* Suppose the planner gives us the following plan:
*
* Nest Loop (DEPT.mgr = EMP.name)
* / \
* / \
* Seq Scan Seq Scan
* DEPT EMP
* (name = "shoe")
*
* ExecStart() is called first.
* It calls InitPlan() which calls ExecInitNode() on
* the root of the plan -- the nest loop node.
* EXAMPLE
* 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:
*
* * ExecInitNode() notices that it is looking at a nest loop and
* as the code below demonstrates, it calls ExecInitNestLoop().
* Eventually this calls ExecInitNode() on the right and left subplans
* and so forth until the entire plan is initialized.
*
* * Then when ExecRun() is called, it calls ExecutePlan() which
* calls ExecProcNode() repeatedly on the top node of the plan.
* Each time this happens, ExecProcNode() will end up calling
* ExecNestLoop(), which calls ExecProcNode() on its subplans.
* Each of these subplans is a sequential scan so ExecSeqScan() is
* called. The slots returned by ExecSeqScan() may contain
* tuples which contain the attributes ExecNestLoop() uses to
* form the tuples it returns.
* retrieve (DEPT.no_emps, EMP.age)
* where EMP.name = DEPT.mgr and
* DEPT.name = "shoe"
*
* * Eventually ExecSeqScan() stops returning tuples and the nest
* loop join ends. Lastly, ExecEnd() calls ExecEndNode() which
* calls ExecEndNestLoop() which in turn calls ExecEndNode() on
* its subplans which result in ExecEndSeqScan().
* Suppose the planner gives us the following plan:
*
* This should show how the executor works by having
* ExecInitNode(), ExecProcNode() and ExecEndNode() dispatch
* their work to the appopriate node support routines which may
* in turn call these routines themselves on their subplans.
* Nest Loop (DEPT.mgr = EMP.name)
* / \
* / \
* Seq Scan Seq Scan
* DEPT EMP
* (name = "shoe")
*
* ExecStart() is called first.
* It calls InitPlan() which calls ExecInitNode() on
* the root of the plan -- the nest loop node.
*
* * ExecInitNode() notices that it is looking at a nest loop and
* as the code below demonstrates, it calls ExecInitNestLoop().
* Eventually this calls ExecInitNode() on the right and left subplans
* and so forth until the entire plan is initialized.
*
* * Then when ExecRun() is called, it calls ExecutePlan() which
* calls ExecProcNode() repeatedly on the top node of the plan.
* Each time this happens, ExecProcNode() will end up calling
* ExecNestLoop(), which calls ExecProcNode() on its subplans.
* Each of these subplans is a sequential scan so ExecSeqScan() is
* called. The slots returned by ExecSeqScan() may contain
* tuples which contain the attributes ExecNestLoop() uses to
* form the tuples it returns.
*
* * Eventually ExecSeqScan() stops returning tuples and the nest
* loop join ends. Lastly, ExecEnd() calls ExecEndNode() which
* calls ExecEndNestLoop() which in turn calls ExecEndNode() on
* its subplans which result in ExecEndSeqScan().
*
* This should show how the executor works by having
* ExecInitNode(), ExecProcNode() and ExecEndNode() dispatch
* their work to the appopriate node support routines which may
* in turn call these routines themselves on their subplans.
*
*/
#include "postgres.h"
@ -91,389 +91,393 @@
#include "executor/nodeTee.h"
/* ------------------------------------------------------------------------
* ExecInitNode
*
* Recursively initializes all the nodes in the plan rooted
* at 'node'.
*
* Initial States:
* 'node' is the plan produced by the query planner
*
* returns TRUE/FALSE on whether the plan was successfully initialized
* ExecInitNode
*
* Recursively initializes all the nodes in the plan rooted
* at 'node'.
*
* Initial States:
* 'node' is the plan produced by the query planner
*
* returns TRUE/FALSE on whether the plan was successfully initialized
* ------------------------------------------------------------------------
*/
bool
ExecInitNode(Plan *node, EState *estate, Plan *parent)
ExecInitNode(Plan * node, EState * estate, Plan * parent)
{
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
* ----------------
*/
case T_SeqScan:
result = ExecInitSeqScan((SeqScan *)node, estate, parent);
break;
case T_IndexScan:
result = ExecInitIndexScan((IndexScan *)node, estate, parent);
break;
/* ----------------
* join nodes
* ----------------
*/
case T_NestLoop:
result = ExecInitNestLoop((NestLoop *)node, estate, parent);
break;
case T_MergeJoin:
result = ExecInitMergeJoin((MergeJoin *)node, estate, parent);
break;
/* ----------------
* materialization nodes
* ----------------
*/
case T_Material:
result = ExecInitMaterial((Material *)node, estate, parent);
break;
case T_Sort:
result = ExecInitSort((Sort *)node, estate, parent);
break;
case T_Unique:
result = ExecInitUnique((Unique *)node, estate, parent);
break;
case T_Group:
result = ExecInitGroup((Group *)node, estate, parent);
break;
bool result;
case T_Agg:
result = ExecInitAgg((Agg *)node, estate, parent);
break;
case T_Hash:
result = ExecInitHash((Hash *)node, estate, parent);
break;
case T_HashJoin:
result = ExecInitHashJoin((HashJoin *)node, estate, parent);
break;
case T_Tee:
result = ExecInitTee((Tee*)node, estate, parent);
break;
/* ----------------
* do nothing when we get to the end
* of a leaf on tree.
* ----------------
*/
if (node == NULL)
return FALSE;
default:
elog(DEBUG, "ExecInitNode: node not yet supported: %d",
nodeTag(node));
result = FALSE;
}
return result;
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
* ----------------
*/
case T_SeqScan:
result = ExecInitSeqScan((SeqScan *) node, estate, parent);
break;
case T_IndexScan:
result = ExecInitIndexScan((IndexScan *) node, estate, parent);
break;
/* ----------------
* join nodes
* ----------------
*/
case T_NestLoop:
result = ExecInitNestLoop((NestLoop *) node, estate, parent);
break;
case T_MergeJoin:
result = ExecInitMergeJoin((MergeJoin *) node, estate, parent);
break;
/* ----------------
* materialization nodes
* ----------------
*/
case T_Material:
result = ExecInitMaterial((Material *) node, estate, parent);
break;
case T_Sort:
result = ExecInitSort((Sort *) node, estate, parent);
break;
case T_Unique:
result = ExecInitUnique((Unique *) node, estate, parent);
break;
case T_Group:
result = ExecInitGroup((Group *) node, estate, parent);
break;
case T_Agg:
result = ExecInitAgg((Agg *) node, estate, parent);
break;
case T_Hash:
result = ExecInitHash((Hash *) node, estate, parent);
break;
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
*
* Initial States:
* the query tree must be initialized once by calling ExecInit.
* ExecProcNode
*
* Initial States:
* the query tree must be initialized once by calling ExecInit.
* ----------------------------------------------------------------
*/
TupleTableSlot *
ExecProcNode(Plan *node, Plan *parent)
ExecProcNode(Plan * node, Plan * parent)
{
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
* ----------------
*/
case T_SeqScan:
result = ExecSeqScan((SeqScan *)node);
break;
TupleTableSlot *result;
case T_IndexScan:
result = ExecIndexScan((IndexScan *)node);
break;
/* ----------------
* join nodes
* deal with NULL nodes..
* ----------------
*/
case T_NestLoop:
result = ExecNestLoop((NestLoop *)node, parent);
break;
case T_MergeJoin:
result = ExecMergeJoin((MergeJoin *)node);
break;
/* ----------------
* materialization nodes
* ----------------
*/
case T_Material:
result = ExecMaterial((Material *)node);
break;
case T_Sort:
result = ExecSort((Sort *)node);
break;
case T_Unique:
result = ExecUnique((Unique *)node);
break;
case T_Group:
result = ExecGroup((Group *)node);
break;
if (node == NULL)
return NULL;
case T_Agg:
result = ExecAgg((Agg *)node);
break;
case T_Hash:
result = ExecHash((Hash *)node);
break;
case T_HashJoin:
result = ExecHashJoin((HashJoin *)node);
break;
case T_Tee:
result = ExecTee((Tee*)node, parent);
break;
switch (nodeTag(node))
{
/* ----------------
* control nodes
* ----------------
*/
case T_Result:
result = ExecResult((Result *) node);
break;
default:
elog(DEBUG, "ExecProcNode: node not yet supported: %d",
nodeTag(node));
result = FALSE;
}
return result;
case T_Append:
result = ExecProcAppend((Append *) node);
break;
/* ----------------
* scan nodes
* ----------------
*/
case T_SeqScan:
result = ExecSeqScan((SeqScan *) node);
break;
case T_IndexScan:
result = ExecIndexScan((IndexScan *) node);
break;
/* ----------------
* join nodes
* ----------------
*/
case T_NestLoop:
result = ExecNestLoop((NestLoop *) node, parent);
break;
case T_MergeJoin:
result = ExecMergeJoin((MergeJoin *) node);
break;
/* ----------------
* materialization nodes
* ----------------
*/
case T_Material:
result = ExecMaterial((Material *) node);
break;
case T_Sort:
result = ExecSort((Sort *) node);
break;
case T_Unique:
result = ExecUnique((Unique *) node);
break;
case T_Group:
result = ExecGroup((Group *) node);
break;
case T_Agg:
result = ExecAgg((Agg *) node);
break;
case T_Hash:
result = ExecHash((Hash *) node);
break;
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
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;
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
*
* Recursively cleans up all the nodes in the plan rooted
* at 'node'.
/* ----------------------------------------------------------------
* ExecEndNode
*
* After this operation, the query plan will not be able to
* processed any further. This should be called only after
* the query plan has been fully executed.
* ----------------------------------------------------------------
* Recursively cleans up all the nodes in the plan rooted
* at 'node'.
*
* After this operation, the query plan will not be able to
* processed any further. This should be called only after
* the query plan has been fully executed.
* ----------------------------------------------------------------
*/
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:
ExecEndResult((Result *)node);
break;
case T_Append:
ExecEndAppend((Append *)node);
break;
/* ----------------
* scan nodes
* ----------------
*/
case T_SeqScan:
ExecEndSeqScan((SeqScan *)node);
break;
if (node == NULL)
return;
case T_IndexScan:
ExecEndIndexScan((IndexScan *)node);
break;
/* ----------------
* join nodes
* ----------------
*/
case T_NestLoop:
ExecEndNestLoop((NestLoop *)node);
break;
case T_MergeJoin:
ExecEndMergeJoin((MergeJoin *)node);
break;
/* ----------------
* materialization nodes
* ----------------
*/
case T_Material:
ExecEndMaterial((Material *)node);
break;
case T_Sort:
ExecEndSort((Sort *)node);
break;
case T_Unique:
ExecEndUnique((Unique *)node);
break;
case T_Group:
ExecEndGroup((Group *)node);
break;
switch (nodeTag(node))
{
/* ----------------
* control nodes
* ----------------
*/
case T_Result:
ExecEndResult((Result *) node);
break;
case T_Agg:
ExecEndAgg((Agg *)node);
break;
/* ----------------
* XXX add hooks to these
* ----------------
*/
case T_Hash:
ExecEndHash((Hash *) node);
break;
case T_HashJoin:
ExecEndHashJoin((HashJoin *) node);
break;
case T_Tee:
ExecEndTee((Tee*) node, parent);
break;
case T_Append:
ExecEndAppend((Append *) node);
break;
default:
elog(DEBUG, "ExecEndNode: node not yet supported",
nodeTag(node));
break;
}
/* ----------------
* scan nodes
* ----------------
*/
case T_SeqScan:
ExecEndSeqScan((SeqScan *) node);
break;
case T_IndexScan:
ExecEndIndexScan((IndexScan *) node);
break;
/* ----------------
* join nodes
* ----------------
*/
case T_NestLoop:
ExecEndNestLoop((NestLoop *) node);
break;
case T_MergeJoin:
ExecEndMergeJoin((MergeJoin *) node);
break;
/* ----------------
* materialization nodes
* ----------------
*/
case T_Material:
ExecEndMaterial((Material *) node);
break;
case T_Sort:
ExecEndSort((Sort *) node);
break;
case T_Unique:
ExecEndUnique((Unique *) node);
break;
case T_Group:
ExecEndGroup((Group *) node);
break;
case T_Agg:
ExecEndAgg((Agg *) node);
break;
/* ----------------
* XXX add hooks to these
* ----------------
*/
case T_Hash:
ExecEndHash((Hash *) node);
break;
case T_HashJoin:
ExecEndHashJoin((HashJoin *) node);
break;
case T_Tee:
ExecEndTee((Tee *) node, parent);
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--
* This code provides support for generalized relation scans. ExecScan
* 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
* stuff - checking the qualification and projecting the tuple
* appropriately.
* This code provides support for generalized relation scans. ExecScan
* 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
* stuff - checking the qualification and projecting the tuple
* appropriately.
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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"
/* ----------------------------------------------------------------
* ExecScan
*
* Scans the relation using the 'access method' indicated and
* returns the next qualifying tuple in the direction specified
* in the global variable ExecDirection.
* The access method returns the next tuple and execScan() is
* responisble for checking the tuple returned against the qual-clause.
*
* Conditions:
* -- the "cursor" maintained by the AMI is positioned at the tuple
* returned previously.
*
* Initial States:
* -- the relation indicated is opened for scanning so that the
* "cursor" is positioned before the first qualifying tuple.
* ExecScan
*
* May need to put startmmgr and endmmgr in here.
* Scans the relation using the 'access method' indicated and
* returns the next qualifying tuple in the direction specified
* in the global variable ExecDirection.
* The access method returns the next tuple and execScan() is
* responisble for checking the tuple returned against the qual-clause.
*
* Conditions:
* -- the "cursor" maintained by the AMI is positioned at the tuple
* returned previously.
*
* Initial States:
* -- the relation indicated is opened for scanning so that the
* "cursor" is positioned before the first qualifying tuple.
*
* May need to put startmmgr and endmmgr in here.
* ----------------------------------------------------------------
*/
TupleTableSlot *
ExecScan(Scan *node,
TupleTableSlot* (*accessMtd)()) /* function returning a tuple */
ExecScan(Scan * node,
TupleTableSlot * (*accessMtd) ()) /* function returning a
* tuple */
{
CommonScanState *scanstate;
EState *estate;
List *qual;
bool isDone;
CommonScanState *scanstate;
EState *estate;
List *qual;
bool isDone;
TupleTableSlot *slot;
TupleTableSlot *resultSlot;
HeapTuple newTuple;
TupleTableSlot *slot;
TupleTableSlot *resultSlot;
HeapTuple newTuple;
ExprContext *econtext;
ProjectionInfo *projInfo;
ExprContext *econtext;
ProjectionInfo *projInfo;
/* ----------------
* initialize misc variables
* ----------------
*/
newTuple = NULL;
slot = NULL;
/* ----------------
* initialize misc variables
* ----------------
*/
newTuple = NULL;
slot = NULL;
estate = node->plan.state;
scanstate = node->scanstate;
estate = node->plan.state;
scanstate = node->scanstate;
/* ----------------
* get the expression context
* ----------------
*/
econtext = scanstate->cstate.cs_ExprContext;
/* ----------------
* get the expression context
* ----------------
*/
econtext = scanstate->cstate.cs_ExprContext;
/* ----------------
* initialize fields in ExprContext which don't change
* in the course of the scan..
* ----------------
*/
qual = node->plan.qual;
econtext->ecxt_relation = scanstate->css_currentRelation;
econtext->ecxt_relid = node->scanrelid;
/* ----------------
* initialize fields in ExprContext which don't change
* in the course of the scan..
* ----------------
*/
qual = node->plan.qual;
econtext->ecxt_relation = scanstate->css_currentRelation;
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;
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);
scanstate->cstate.cs_TupFromTlist = !isDone;
/* ----------------
* 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;
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--
* Routines to handle functions called from the executor
* Putting this stuff in fmgr makes the postmaster a mess....
* Routines to handle functions called from the executor
* Putting this stuff in fmgr makes the postmaster a mess....
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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
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 {
QueryDesc *qd;
EState *estate;
struct local_es *next;
ExecStatus status;
} execution_state;
typedef struct local_es
{
QueryDesc *qd;
EState *estate;
struct local_es *next;
ExecStatus status;
} execution_state;
#define LAST_POSTQUEL_COMMAND(es) ((es)->next == (execution_state *)NULL)
/* non-export function prototypes */
static TupleDesc postquel_start(execution_state *es);
static execution_state *init_execution_state(FunctionCachePtr fcache,
char *args[]);
static TupleTableSlot *postquel_getnext(execution_state *es);
static void postquel_end(execution_state *es);
static void postquel_sub_params(execution_state *es, int nargs,
char *args[], bool *nullV);
static Datum postquel_execute(execution_state *es, FunctionCachePtr fcache,
List *fTlist, char **args, bool *isNull);
static TupleDesc postquel_start(execution_state * es);
static execution_state *
init_execution_state(FunctionCachePtr fcache,
char *args[]);
static TupleTableSlot *postquel_getnext(execution_state * es);
static void postquel_end(execution_state * es);
static void
postquel_sub_params(execution_state * es, int nargs,
char *args[], bool * nullV);
static Datum
postquel_execute(execution_state * es, FunctionCachePtr fcache,
List * fTlist, char **args, bool * isNull);
Datum
ProjectAttribute(TupleDesc TD,
TargetEntry *tlist,
HeapTuple tup,
bool *isnullP)
TargetEntry * tlist,
HeapTuple tup,
bool * isnullP)
{
Datum val,valueP;
Var *attrVar = (Var *)tlist->expr;
AttrNumber attrno = attrVar->varattno;
val = PointerGetDatum(heap_getattr(tup,
InvalidBuffer,
attrno,
TD,
isnullP));
if (*isnullP)
return (Datum) NULL;
valueP = datumCopy(val,
TD->attrs[attrno-1]->atttypid,
TD->attrs[attrno-1]->attbyval,
(Size) TD->attrs[attrno-1]->attlen);
return valueP;
Datum val,
valueP;
Var *attrVar = (Var *) tlist->expr;
AttrNumber attrno = attrVar->varattno;
val = PointerGetDatum(heap_getattr(tup,
InvalidBuffer,
attrno,
TD,
isnullP));
if (*isnullP)
return (Datum) NULL;
valueP = datumCopy(val,
TD->attrs[attrno - 1]->atttypid,
TD->attrs[attrno - 1]->attbyval,
(Size) TD->attrs[attrno - 1]->attlen);
return valueP;
}
static execution_state *
init_execution_state(FunctionCachePtr fcache,
char *args[])
char *args[])
{
execution_state *newes;
execution_state *nextes;
execution_state *preves;
QueryTreeList *queryTree_list;
int i;
List *planTree_list;
int nargs;
nargs = fcache->nargs;
newes = (execution_state *) palloc(sizeof(execution_state));
nextes = newes;
preves = (execution_state *)NULL;
planTree_list = (List *)
pg_plan(fcache->src, fcache->argOidVect, nargs, &queryTree_list, None);
for (i=0; i < queryTree_list->len; i++) {
EState *estate;
Query *queryTree = (Query*) (queryTree_list->qtrees[i]);
Plan *planTree = lfirst(planTree_list);
if (!nextes)
nextes = (execution_state *) palloc(sizeof(execution_state));
if (preves)
preves->next = nextes;
nextes->next = NULL;
nextes->status = F_EXEC_START;
nextes->qd = CreateQueryDesc(queryTree,
planTree,
None);
estate = CreateExecutorState();
if (nargs > 0) {
int i;
ParamListInfo paramLI;
paramLI =
(ParamListInfo)palloc((nargs+1)*sizeof(ParamListInfoData));
memset(paramLI, 0, nargs*sizeof(ParamListInfoData));
execution_state *newes;
execution_state *nextes;
execution_state *preves;
QueryTreeList *queryTree_list;
int i;
List *planTree_list;
int nargs;
estate->es_param_list_info = paramLI;
for (i=0; i<nargs; paramLI++, i++) {
paramLI->kind = PARAM_NUM;
paramLI->id = i+1;
paramLI->isnull = false;
paramLI->value = (Datum) NULL;
}
paramLI->kind = PARAM_INVALID;
nargs = fcache->nargs;
newes = (execution_state *) palloc(sizeof(execution_state));
nextes = newes;
preves = (execution_state *) NULL;
planTree_list = (List *)
pg_plan(fcache->src, fcache->argOidVect, nargs, &queryTree_list, None);
for (i = 0; i < queryTree_list->len; i++)
{
EState *estate;
Query *queryTree = (Query *) (queryTree_list->qtrees[i]);
Plan *planTree = lfirst(planTree_list);
if (!nextes)
nextes = (execution_state *) palloc(sizeof(execution_state));
if (preves)
preves->next = nextes;
nextes->next = NULL;
nextes->status = F_EXEC_START;
nextes->qd = CreateQueryDesc(queryTree,
planTree,
None);
estate = CreateExecutorState();
if (nargs > 0)
{
int i;
ParamListInfo paramLI;
paramLI =
(ParamListInfo) palloc((nargs + 1) * sizeof(ParamListInfoData));
memset(paramLI, 0, nargs * sizeof(ParamListInfoData));
estate->es_param_list_info = paramLI;
for (i = 0; i < nargs; paramLI++, i++)
{
paramLI->kind = PARAM_NUM;
paramLI->id = i + 1;
paramLI->isnull = false;
paramLI->value = (Datum) NULL;
}
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
postquel_start(execution_state *es)
static TupleDesc
postquel_start(execution_state * es)
{
#ifdef FUNC_UTIL_PATCH
/*
* Do nothing for utility commands. (create, destroy...) DZ - 30-8-1996
*/
if (es->qd->operation == CMD_UTILITY) {
return (TupleDesc) NULL;
}
/*
* Do nothing for utility commands. (create, destroy...) DZ -
* 30-8-1996
*/
if (es->qd->operation == CMD_UTILITY)
{
return (TupleDesc) NULL;
}
#endif
return ExecutorStart(es->qd, es->estate);
return ExecutorStart(es->qd, es->estate);
}
static TupleTableSlot *
postquel_getnext(execution_state *es)
postquel_getnext(execution_state * es)
{
int feature;
int feature;
#ifdef FUNC_UTIL_PATCH
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
if (es->qd->operation == CMD_UTILITY)
{
feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN;
return ExecutorRun(es->qd, es->estate, feature, 0);
}
static void
postquel_end(execution_state *es)
{
#ifdef FUNC_UTIL_PATCH
/*
* Do nothing for utility commands. (create, destroy...) DZ - 30-8-1996
*/
if (es->qd->operation == CMD_UTILITY) {
return;
}
#endif
ExecutorEnd(es->qd, es->estate);
}
static void
postquel_sub_params(execution_state *es,
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)];
/*
* 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;
return ExecutorRun(es->qd, es->estate, feature, 0);
}
static void
postquel_end(execution_state * es)
{
#ifdef FUNC_UTIL_PATCH
/*
* Do nothing for utility commands. (create, destroy...) DZ -
* 30-8-1996
*/
if (es->qd->operation == CMD_UTILITY)
{
return;
}
#endif
ExecutorEnd(es->qd, es->estate);
}
static void
postquel_sub_params(execution_state * es,
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 *
copy_function_result(FunctionCachePtr fcache,
TupleTableSlot *resultSlot)
TupleTableSlot * resultSlot)
{
TupleTableSlot *funcSlot;
TupleDesc resultTd;
HeapTuple newTuple;
HeapTuple oldTuple;
Assert(! TupIsNull(resultSlot));
oldTuple = resultSlot->val;
funcSlot = (TupleTableSlot*)fcache->funcSlot;
if (funcSlot == (TupleTableSlot*)NULL)
return resultSlot;
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] =
(AttributeTupleForm)palloc(ATTRIBUTE_TUPLE_SIZE);
memmove(funcTd->attrs[i],
resultTd->attrs[i],
ATTRIBUTE_TUPLE_SIZE);
i++;
TupleTableSlot *funcSlot;
TupleDesc resultTd;
HeapTuple newTuple;
HeapTuple oldTuple;
Assert(!TupIsNull(resultSlot));
oldTuple = resultSlot->val;
funcSlot = (TupleTableSlot *) fcache->funcSlot;
if (funcSlot == (TupleTableSlot *) NULL)
return resultSlot;
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] =
(AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE);
memmove(funcTd->attrs[i],
resultTd->attrs[i],
ATTRIBUTE_TUPLE_SIZE);
i++;
}
}
}
newTuple = heap_copytuple(oldTuple);
return ExecStoreTuple(newTuple,funcSlot,InvalidBuffer,true);
newTuple = heap_copytuple(oldTuple);
return ExecStoreTuple(newTuple, funcSlot, InvalidBuffer, true);
}
static Datum
postquel_execute(execution_state *es,
FunctionCachePtr fcache,
List *fTlist,
char **args,
bool *isNull)
static Datum
postquel_execute(execution_state * es,
FunctionCachePtr fcache,
List * fTlist,
char **args,
bool * isNull)
{
TupleTableSlot *slot;
Datum value;
TupleTableSlot *slot;
Datum value;
#ifdef INDEXSCAN_PATCH
/*
* It's more right place to do it (before postquel_start->ExecutorStart).
* Now ExecutorStart->ExecInitIndexScan->ExecEvalParam works ok.
* (But note: I HOPE we can do it here). - vadim 01/22/97
*/
if (fcache->nargs > 0)
postquel_sub_params(es, fcache->nargs, args, fcache->nullVect);
/*
* It's more right place to do it (before
* postquel_start->ExecutorStart). Now
* ExecutorStart->ExecInitIndexScan->ExecEvalParam works ok. (But
* note: I HOPE we can do it here). - vadim 01/22/97
*/
if (fcache->nargs > 0)
postquel_sub_params(es, fcache->nargs, args, fcache->nullVect);
#endif
if (es->status == F_EXEC_START)
if (es->status == F_EXEC_START)
{
postquel_start(es);
es->status = F_EXEC_RUN;
postquel_start(es);
es->status = F_EXEC_RUN;
}
#ifndef INDEXSCAN_PATCH
if (fcache->nargs > 0)
postquel_sub_params(es, fcache->nargs, args, fcache->nullVect);
if (fcache->nargs > 0)
postquel_sub_params(es, fcache->nargs, args, fcache->nullVect);
#endif
slot = postquel_getnext(es);
if (TupIsNull(slot)) {
postquel_end(es);
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;
/*
* Copy the result. copy_function_result is smart enough
* to do nothing when no action is called for. This helps
* 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,
tle,
tup,
isNull);
}else {
value = (Datum)resSlot;
*isNull = false;
slot = postquel_getnext(es);
if (TupIsNull(slot))
{
postquel_end(es);
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 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;
if (LAST_POSTQUEL_COMMAND(es))
{
TupleTableSlot *resSlot;
/*
* Copy the result. copy_function_result is smart enough to do
* nothing when no action is called for. This helps 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,
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;
}
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;
/*
* 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
postquel_function(Func *funcNode, char **args, bool *isNull, bool *isDone)
postquel_function(Func * funcNode, char **args, bool * isNull, bool * isDone)
{
execution_state *es;
Datum result = 0;
FunctionCachePtr fcache = funcNode->func_fcache;
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)
{
execution_state *es;
Datum result = 0;
FunctionCachePtr fcache = funcNode->func_fcache;
CommandId savedId;
/*
* 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;
while (es)
savedId = GetScanCommandId();
SetScanCommandId(GetCurrentCommandId());
es = (execution_state *) fcache->func_state;
if (es == NULL)
{
es->status = F_EXEC_START;
es = es->next;
es = init_execution_state(fcache, args);
fcache->func_state = (char *) es;
}
while (es && es->status == F_EXEC_DONE)
es = es->next;
Assert(es);
/*
* Let caller know we're finished.
* 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.
*/
*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;
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--
* routines to handle append nodes.
* routines to handle append nodes.
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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
* ExecInitAppend - initialize the append node
* ExecProcAppend - retrieve the next tuple from the node
* ExecEndAppend - shut down the append node
* ExecInitAppend - initialize the append node
* ExecProcAppend - retrieve the next tuple from the node
* ExecEndAppend - shut down the append node
*
* NOTES
* Each append node contains a list of one or more subplans which
* must be iteratively processed (forwards or backwards).
* Tuples are retrieved by executing the 'whichplan'th subplan
* until the subplan stops returning tuples, at which point that
* plan is shut down and the next started up.
* NOTES
* Each append node contains a list of one or more subplans which
* must be iteratively processed (forwards or backwards).
* Tuples are retrieved by executing the 'whichplan'th subplan
* until the subplan stops returning tuples, at which point that
* plan is shut down and the next started up.
*
* Append nodes don't make use of their left and right
* subtrees, rather they maintain a list of subplans so
* a typical append node looks like this in the plan tree:
* Append nodes don't make use of their left and right
* subtrees, rather they maintain a list of subplans so
* a typical append node looks like this in the plan tree:
*
* ...
* /
* Append -------+------+------+--- nil
* / \ | | |
* nil nil ... ... ...
* subplans
* ...
* /
* Append -------+------+------+--- nil
* / \ | | |
* nil nil ... ... ...
* subplans
*
* Append nodes are currently used to support inheritance
* queries, where several relations need to be scanned.
* For example, in our standard person/student/employee/student-emp
* example, where student and employee inherit from person
* and student-emp inherits from student and employee, the
* query:
* Append nodes are currently used to support inheritance
* queries, where several relations need to be scanned.
* For example, in our standard person/student/employee/student-emp
* example, where student and employee inherit from person
* and student-emp inherits from student and employee, the
* query:
*
* retrieve (e.name) from e in person*
* retrieve (e.name) from e in person*
*
* generates the plan:
* generates the plan:
*
* |
* Append -------+-------+--------+--------+
* / \ | | | |
* nil nil Scan Scan Scan Scan
* | | | |
* person employee student student-emp
* |
* Append -------+-------+--------+--------+
* / \ | | | |
* nil nil Scan Scan Scan Scan
* | | | |
* person employee student student-emp
*/
#include "postgres.h"
@ -62,429 +62,451 @@
#include "executor/nodeIndexscan.h"
#include "utils/palloc.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
*
* Sets up the append node state (i.e. the append state node)
* for the "next" scan.
*
* Returns t iff there is a "next" scan to process.
* exec-append-initialize-next
*
* Sets up the append node state (i.e. the append state node)
* for the "next" scan.
*
* Returns t iff there is a "next" scan to process.
* ----------------------------------------------------------------
*/
static bool
exec_append_initialize_next(Append *node)
static bool
exec_append_initialize_next(Append * node)
{
EState *estate;
AppendState *unionstate;
TupleTableSlot *result_slot;
List *rangeTable;
int whichplan;
int nplans;
List *rtentries;
ResTarget *rtentry;
Index unionrelid;
/* ----------------
* get information from the append node
* ----------------
*/
estate = node->plan.state;
unionstate = node->unionstate;
result_slot = unionstate->cstate.cs_ResultTupleSlot;
rangeTable = estate->es_range_table;
whichplan = unionstate->as_whichplan;
nplans = unionstate->as_nplans;
rtentries = node->unionrtentries;
if (whichplan < 0) {
/* ----------------
* if scanning in reverse, we start at
* the last scan in the list and then
* proceed back to the first.. in any case
* we inform ExecProcAppend that we are
* at the end of the line by returning 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;
EState *estate;
AppendState *unionstate;
TupleTableSlot *result_slot;
List *rangeTable;
rt_store(unionrelid, rangeTable, rtentry);
int whichplan;
int nplans;
List *rtentries;
ResTarget *rtentry;
Index unionrelid;
/* ----------------
* get information from the append node
* ----------------
*/
estate = node->plan.state;
unionstate = node->unionstate;
result_slot = unionstate->cstate.cs_ResultTupleSlot;
rangeTable = estate->es_range_table;
whichplan = unionstate->as_whichplan;
nplans = unionstate->as_nplans;
rtentries = node->unionrtentries;
if (whichplan < 0)
{
/* ----------------
* if scanning in reverse, we start at
* the last scan in the list and then
* proceed back to the first.. in any case
* we inform ExecProcAppend that we are
* at the end of the line by returning FALSE
* ----------------
*/
unionstate->as_whichplan = 0;
return FALSE;
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;
}
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;
}
return TRUE;
}
}
/* ----------------------------------------------------------------
* ExecInitAppend
*
* Begins all of the subscans of the append node, storing the
* scan structures in the 'initialized' vector of the append-state
* structure.
* ExecInitAppend
*
* (This is potentially wasteful, since the entire result of the
* append node may not be scanned, but this way all of the
* structures get allocated in the executor's top level memory
* block instead of that of the call to ExecProcAppend.)
*
* Returns the scan result of the first scan.
* Begins all of the subscans of the append node, storing the
* scan structures in the 'initialized' vector of the append-state
* structure.
*
* (This is potentially wasteful, since the entire result of the
* append node may not be scanned, but this way all of the
* structures get allocated in the executor's top level memory
* block instead of that of the call to ExecProcAppend.)
*
* Returns the scan result of the first scan.
* ----------------------------------------------------------------
*/
bool
ExecInitAppend(Append *node, EState *estate, Plan *parent)
ExecInitAppend(Append * node, EState * estate, Plan * parent)
{
AppendState *unionstate;
int nplans;
List *resultList = NULL;
List *rtentries;
List *unionplans;
bool *initialized;
int i;
Plan *initNode;
List *junkList;
RelationInfo *es_rri = estate->es_result_relation_info;
/* ----------------
* assign execution state to node and get information
* for append state
* ----------------
*/
node->plan.state = estate;
unionplans = node->unionplans;
nplans = length(unionplans);
rtentries = node->unionrtentries;
CXT1_printf("ExecInitAppend: context is %d\n", CurrentMemoryContext);
initialized = (bool *)palloc(nplans * sizeof(bool));
/* ----------------
* create new AppendState for our append node
* ----------------
*/
unionstate = makeNode(AppendState);
unionstate->as_whichplan = 0;
unionstate->as_nplans = nplans;
unionstate->as_initialized = initialized;
unionstate->as_rtentries = rtentries;
AppendState *unionstate;
int nplans;
List *resultList = NULL;
List *rtentries;
List *unionplans;
bool *initialized;
int i;
Plan *initNode;
List *junkList;
RelationInfo *es_rri = estate->es_result_relation_info;
node->unionstate = unionstate;
/* ----------------
* Miscellanious initialization
*
* + assign node's base_id
* + assign debugging hooks
*
* Append plans don't have expression contexts because they
* never call ExecQual or ExecTargetList.
* ----------------
*/
ExecAssignNodeBaseInfo(estate, &unionstate->cstate, parent);
#define APPEND_NSLOTS 1
/* ----------------
* append nodes still have Result slots, which hold pointers
* 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
* ---------------
/* ----------------
* assign execution state to node and get information
* for append state
* ----------------
*/
if ((es_rri != (RelationInfo*)NULL) &&
(node->unionrelid == es_rri->ri_RangeTableIndex)) {
targetList = initNode->targetlist;
j = (JunkFilter *) ExecInitJunkFilter(targetList);
junkList = lappend(junkList, j);
node->plan.state = estate;
unionplans = node->unionplans;
nplans = length(unionplans);
rtentries = node->unionrtentries;
CXT1_printf("ExecInitAppend: context is %d\n", CurrentMemoryContext);
initialized = (bool *) palloc(nplans * sizeof(bool));
/* ----------------
* create new AppendState for our append node
* ----------------
*/
unionstate = makeNode(AppendState);
unionstate->as_whichplan = 0;
unionstate->as_nplans = nplans;
unionstate->as_initialized = initialized;
unionstate->as_rtentries = rtentries;
node->unionstate = unionstate;
/* ----------------
* Miscellanious initialization
*
* + assign node's base_id
* + assign debugging hooks
*
* Append plans don't have expression contexts because they
* never call ExecQual or ExecTargetList.
* ----------------
*/
ExecAssignNodeBaseInfo(estate, &unionstate->cstate, parent);
#define APPEND_NSLOTS 1
/* ----------------
* append nodes still have Result slots, which hold pointers
* 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;
}
}
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);
/* ----------------
* 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) &&
(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
result = (List *) initialized[0];
#endif
return TRUE;
result = (List *) initialized[0];
#endif
return TRUE;
}
int
ExecCountSlotsAppend(Append *node)
ExecCountSlotsAppend(Append * node)
{
List *plan;
List *unionplans = node->unionplans;
int nSlots = 0;
foreach (plan,unionplans) {
nSlots += ExecCountSlotsNode((Plan *)lfirst(plan));
}
return nSlots + APPEND_NSLOTS;
List *plan;
List *unionplans = node->unionplans;
int nSlots = 0;
foreach(plan, unionplans)
{
nSlots += ExecCountSlotsNode((Plan *) lfirst(plan));
}
return nSlots + APPEND_NSLOTS;
}
/* ----------------------------------------------------------------
* ExecProcAppend
*
* Handles the iteration over the multiple scans.
*
* NOTE: Can't call this ExecAppend, that name is used in execMain.l
* ExecProcAppend
*
* Handles the iteration over the multiple scans.
*
* NOTE: Can't call this ExecAppend, that name is used in execMain.l
* ----------------------------------------------------------------
*/
TupleTableSlot *
ExecProcAppend(Append *node)
ExecProcAppend(Append * node)
{
EState *estate;
AppendState *unionstate;
int whichplan;
List *unionplans;
Plan *subnode;
TupleTableSlot *result;
TupleTableSlot *result_slot;
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;
}
EState *estate;
AppendState *unionstate;
int whichplan;
List *unionplans;
Plan *subnode;
TupleTableSlot *result;
TupleTableSlot *result_slot;
ScanDirection direction;
/* ----------------
* return something from next node or an empty slot
* all of our subplans have been exhausted.
* get information from the node
* ----------------
*/
if (exec_append_initialize_next(node)) {
ExecSetSlotDescriptorIsNew(result_slot, true);
return
ExecProcAppend(node);
} else
return ExecClearTuple(result_slot);
}
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
* 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
*
* Shuts down the subscans of the append node.
*
* Returns nothing of interest.
* ExecEndAppend
*
* Shuts down the subscans of the append node.
*
* Returns nothing of interest.
* ----------------------------------------------------------------
*/
void
ExecEndAppend(Append *node)
ExecEndAppend(Append * node)
{
AppendState *unionstate;
int nplans;
List *unionplans;
bool *initialized;
int i;
List *resultRelationInfoList;
RelationInfo *resultRelationInfo;
AppendState *unionstate;
int nplans;
List *unionplans;
bool *initialized;
int i;
List *resultRelationInfoList;
RelationInfo *resultRelationInfo;
/* ----------------
* get information from the node
* ----------------
*/
unionstate = node->unionstate;
unionplans = node->unionplans;
nplans = unionstate->as_nplans;
initialized = unionstate->as_initialized;
/* ----------------
* shut down each of the subscans
* ----------------
*/
for(i = 0; i < nplans; i++) {
if (initialized[i]==TRUE) {
ExecEndNode( (Plan *) nth(i, unionplans), (Plan*)node );
}
}
/* ----------------
* close out the different result relations
* ----------------
*/
resultRelationInfoList = unionstate->as_result_relation_info_list;
while (resultRelationInfoList != NIL) {
Relation resultRelationDesc;
resultRelationInfo = (RelationInfo*) lfirst(resultRelationInfoList);
resultRelationDesc = resultRelationInfo->ri_RelationDesc;
heap_close(resultRelationDesc);
pfree(resultRelationInfo);
resultRelationInfoList = lnext(resultRelationInfoList);
}
if (unionstate->as_result_relation_info_list)
pfree(unionstate->as_result_relation_info_list);
/* ----------------
* get information from the node
* ----------------
*/
unionstate = node->unionstate;
unionplans = node->unionplans;
nplans = unionstate->as_nplans;
initialized = unionstate->as_initialized;
/* XXX should free unionstate->as_rtentries and unionstate->as_junkfilter_list here */
/* ----------------
* shut down each of the subscans
* ----------------
*/
for (i = 0; i < nplans; i++)
{
if (initialized[i] == TRUE)
{
ExecEndNode((Plan *) nth(i, unionplans), (Plan *) node);
}
}
/* ----------------
* close out the different result relations
* ----------------
*/
resultRelationInfoList = unionstate->as_result_relation_info_list;
while (resultRelationInfoList != NIL)
{
Relation resultRelationDesc;
resultRelationInfo = (RelationInfo *) lfirst(resultRelationInfoList);
resultRelationDesc = resultRelationInfo->ri_RelationDesc;
heap_close(resultRelationDesc);
pfree(resultRelationInfo);
resultRelationInfoList = lnext(resultRelationInfoList);
}
if (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
*/
}

View File

@ -1,19 +1,19 @@
/*-------------------------------------------------------------------------
*
* 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
*
*
* DESCRIPTION
* 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
* back from the outer plan is sorted in the order specified by the group
* columns. (ie. tuples from the same group are consecutive)
* 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
* back from the outer plan is sorted in the order specified by the group
* columns. (ie. tuples from the same group are consecutive)
*
* 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/nodeGroup.h"
static TupleTableSlot *ExecGroupEveryTuple(Group *node);
static TupleTableSlot *ExecGroupOneTuple(Group *node);
static bool sameGroup(TupleTableSlot *oldslot, TupleTableSlot *newslot,
int numCols, AttrNumber *grpColIdx, TupleDesc tupdesc);
static TupleTableSlot *ExecGroupEveryTuple(Group * node);
static TupleTableSlot *ExecGroupOneTuple(Group * node);
static bool
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
* tuplePerGroup is TRUE, every tuple from the same group will be
* 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
* group. (eg. SELECT salary, count{*} FROM emp GROUP BY salary)
* There are two modes in which tuples are returned by ExecGroup. If
* tuplePerGroup is TRUE, every tuple from the same group will be
* 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
* group. (eg. SELECT salary, count{*} FROM emp GROUP BY salary)
*
* If tuplePerGroup is FALSE, only one tuple per group is returned. The
* tuple returned contains only the group columns. NULL is returned only
* at the end when no more groups is present. This is useful when
* the query does not involve aggregates. (eg. SELECT salary FROM emp
* GROUP BY salary)
* If tuplePerGroup is FALSE, only one tuple per group is returned. The
* tuple returned contains only the group columns. NULL is returned only
* at the end when no more groups is present. This is useful when
* the query does not involve aggregates. (eg. SELECT salary FROM emp
* GROUP BY salary)
* ------------------------------------------
*/
TupleTableSlot *
ExecGroup(Group *node)
ExecGroup(Group * node)
{
if (node->tuplePerGroup)
return ExecGroupEveryTuple(node);
else
return ExecGroupOneTuple(node);
if (node->tuplePerGroup)
return ExecGroupEveryTuple(node);
else
return ExecGroupOneTuple(node);
}
/*
* ExecGroupEveryTuple -
* return every tuple with a NULL between each group
* return every tuple with a NULL between each group
*/
static TupleTableSlot *
ExecGroupEveryTuple(Group *node)
ExecGroupEveryTuple(Group * node)
{
GroupState *grpstate;
EState *estate;
ExprContext *econtext;
GroupState *grpstate;
EState *estate;
ExprContext *econtext;
HeapTuple outerTuple = NULL;
TupleTableSlot *outerslot, *lastslot;
ProjectionInfo *projInfo;
TupleTableSlot *resultSlot;
HeapTuple outerTuple = NULL;
TupleTableSlot *outerslot,
*lastslot;
ProjectionInfo *projInfo;
TupleTableSlot *resultSlot;
bool isDone;
bool isDone;
/* ---------------------
* 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
/* ---------------------
* get state info from node
* ---------------------
*/
grpstate->grp_useLastTuple = FALSE;
grpstate = node->grpstate;
if (grpstate->grp_done)
return NULL;
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;
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;
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
* the same group.
* form a projection tuple, store it in the result tuple
* slot and return it.
* ----------------
*/
lastslot = grpstate->csstate.css_ScanTupleSlot;
projInfo = grpstate->csstate.cstate.cs_ProjInfo;
if (lastslot->val != NULL &&
(!sameGroup(lastslot, outerslot,
node->numCols, node->grpColIdx,
ExecGetScanType(&grpstate->csstate)))) {
/* ExecGetResultType(&grpstate->csstate.cstate)))) {*/
econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
resultSlot = ExecProject(projInfo, &isDone);
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);
}
/* ----------------
* 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;
return resultSlot;
}
/*
* ExecGroupOneTuple -
* returns one tuple per group, a NULL at the end when there are no more
* tuples.
* returns one tuple per group, a NULL at the end when there are no more
* tuples.
*/
static TupleTableSlot *
ExecGroupOneTuple(Group *node)
ExecGroupOneTuple(Group * node)
{
GroupState *grpstate;
EState *estate;
ExprContext *econtext;
GroupState *grpstate;
EState *estate;
ExprContext *econtext;
HeapTuple outerTuple = NULL;
TupleTableSlot *outerslot, *lastslot;
ProjectionInfo *projInfo;
TupleTableSlot *resultSlot;
HeapTuple outerTuple = NULL;
TupleTableSlot *outerslot,
*lastslot;
ProjectionInfo *projInfo;
TupleTableSlot *resultSlot;
bool isDone;
bool isDone;
/* ---------------------
* get state info from node
* ---------------------
*/
grpstate = node->grpstate;
if (grpstate->grp_done)
return NULL;
/* ---------------------
* get state info from node
* ---------------------
*/
grpstate = node->grpstate;
if (grpstate->grp_done)
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) {
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;
if (grpstate->grp_useLastTuple)
{
grpstate->grp_useLastTuple = FALSE;
ExecStoreTuple(grpstate->grp_lastSlot->val,
grpstate->csstate.css_ScanTupleSlot,
grpstate->grp_lastSlot->ttc_buffer,
false);
}
ExecStoreTuple(outerTuple,
grpstate->csstate.css_ScanTupleSlot,
outerslot->ttc_buffer,
false);
}
lastslot = grpstate->csstate.css_ScanTupleSlot;
else
{
outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
if (outerslot)
outerTuple = outerslot->val;
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
*/
for(;;) {
outerslot = ExecProcNode(outerPlan(node), (Plan*)node);
outerTuple = (outerslot) ? outerslot->val : NULL;
if (!HeapTupleIsValid(outerTuple)) {
/*
* we have at least one tuple (lastslot) if we reach here
*/
grpstate->grp_done = TRUE;
/*
* find all tuples that belong to a group
*/
for (;;)
{
outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
outerTuple = (outerslot) ? outerslot->val : NULL;
if (!HeapTupleIsValid(outerTuple))
{
/* 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;
}
/* ----------------
* Compare with last tuple and see if this tuple is of
* the same group.
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.
* ----------------
*/
if ((!sameGroup(lastslot, outerslot,
node->numCols, node->grpColIdx,
ExecGetScanType(&grpstate->csstate)))) {
/* ExecGetResultType(&grpstate->csstate.cstate)))) {*/
projInfo = grpstate->csstate.cstate.cs_ProjInfo;
grpstate->grp_useLastTuple = TRUE;
econtext->ecxt_scantuple = lastslot;
resultSlot = ExecProject(projInfo, &isDone);
/* 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,
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;
return resultSlot;
}
/* -----------------
* ExecInitGroup
* ExecInitGroup
*
* Creates the run-time information for the group node produced by the
* planner and initializes its outer subtree
* Creates the run-time information for the group node produced by the
* planner and initializes its outer subtree
* -----------------
*/
bool
ExecInitGroup(Group *node, EState *estate, Plan *parent)
ExecInitGroup(Group * node, EState * estate, Plan * parent)
{
GroupState *grpstate;
Plan *outerPlan;
/*
* assign the node's execution state
*/
node->plan.state = estate;
/*
* create state structure
*/
grpstate = makeNode(GroupState);
node->grpstate = grpstate;
grpstate->grp_useLastTuple = FALSE;
grpstate->grp_done = FALSE;
GroupState *grpstate;
Plan *outerPlan;
/*
* assign the node's execution state
*/
node->plan.state = estate;
/*
* create state structure
*/
grpstate = makeNode(GroupState);
node->grpstate = grpstate;
grpstate->grp_useLastTuple = FALSE;
grpstate->grp_done = FALSE;
/*
* assign node's base id and create expression context
*/
ExecAssignNodeBaseInfo(estate, &grpstate->csstate.cstate,
(Plan *) parent);
ExecAssignExprContext(estate, &grpstate->csstate.cstate);
/*
* assign node's base id and create expression context
*/
ExecAssignNodeBaseInfo(estate, &grpstate->csstate.cstate,
(Plan*) parent);
ExecAssignExprContext(estate, &grpstate->csstate.cstate);
#define GROUP_NSLOTS 2
/*
* tuple table initialization
*/
ExecInitScanTupleSlot(estate, &grpstate->csstate);
ExecInitResultTupleSlot(estate, &grpstate->csstate.cstate);
/*
* initializes child nodes
*/
outerPlan = outerPlan(node);
ExecInitNode(outerPlan, estate, (Plan *)node);
/* ----------------
* initialize tuple type.
* ----------------
*/
ExecAssignScanTypeFromOuterPlan((Plan *) node, &grpstate->csstate);
/*
* tuple table initialization
*/
ExecInitScanTupleSlot(estate, &grpstate->csstate);
ExecInitResultTupleSlot(estate, &grpstate->csstate.cstate);
/*
* 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);
/*
* initializes child nodes
*/
outerPlan = outerPlan(node);
ExecInitNode(outerPlan, estate, (Plan *) node);
return TRUE;
/* ----------------
* initialize tuple type.
* ----------------
*/
ExecAssignScanTypeFromOuterPlan((Plan *) node, &grpstate->csstate);
/*
* 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
ExecCountSlotsGroup(Group *node)
ExecCountSlotsGroup(Group * node)
{
return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
}
/* ------------------------
* ExecEndGroup(node)
* ExecEndGroup(node)
*
* -----------------------
*/
void
ExecEndGroup(Group *node)
ExecEndGroup(Group * node)
{
GroupState *grpstate;
Plan *outerPlan;
GroupState *grpstate;
Plan *outerPlan;
grpstate = node->grpstate;
grpstate = node->grpstate;
ExecFreeProjectionInfo(&grpstate->csstate.cstate);
ExecFreeProjectionInfo(&grpstate->csstate.cstate);
outerPlan = outerPlan(node);
ExecEndNode(outerPlan, (Plan*)node);
/* clean up tuple table */
ExecClearTuple(grpstate->csstate.css_ScanTupleSlot);
outerPlan = outerPlan(node);
ExecEndNode(outerPlan, (Plan *) node);
/* clean up tuple table */
ExecClearTuple(grpstate->csstate.css_ScanTupleSlot);
}
/*****************************************************************************
@ -360,54 +379,63 @@ ExecEndGroup(Group *node)
/*
* code swiped from nodeUnique.c
*/
static bool
sameGroup(TupleTableSlot *oldslot,
TupleTableSlot *newslot,
int numCols,
AttrNumber *grpColIdx,
TupleDesc tupdesc)
static bool
sameGroup(TupleTableSlot * oldslot,
TupleTableSlot * newslot,
int numCols,
AttrNumber * grpColIdx,
TupleDesc tupdesc)
{
bool isNull1,isNull2;
char *attr1, *attr2;
char *val1, *val2;
int i;
AttrNumber att;
Oid typoutput;
bool isNull1,
isNull2;
char *attr1,
*attr2;
char *val1,
*val2;
int i;
AttrNumber att;
Oid typoutput;
for(i = 0; i < numCols; i++) {
att = grpColIdx[i];
typoutput = typtoout((Oid)tupdesc->attrs[att-1]->atttypid);
for (i = 0; i < numCols; i++)
{
att = grpColIdx[i];
typoutput = typtoout((Oid) tupdesc->attrs[att - 1]->atttypid);
attr1 = heap_getattr(oldslot->val,
InvalidBuffer,
att,
tupdesc,
&isNull1);
attr1 = heap_getattr(oldslot->val,
InvalidBuffer,
att,
tupdesc,
&isNull1);
attr2 = heap_getattr(newslot->val,
InvalidBuffer,
att,
tupdesc,
&isNull2);
if (isNull1 == isNull2) {
if (isNull1) /* both are null, they are equal */
continue;
attr2 = heap_getattr(newslot->val,
InvalidBuffer,
att,
tupdesc,
&isNull2);
val1 = fmgr(typoutput, attr1,
gettypelem(tupdesc->attrs[att-1]->atttypid));
val2 = fmgr(typoutput, attr2,
gettypelem(tupdesc->attrs[att-1]->atttypid));
if (isNull1 == isNull2)
{
if (isNull1) /* both are null, they are equal */
continue;
/* now, val1 and val2 are ascii representations so we can
use strcmp for comparison */
if (strcmp(val1,val2) != 0)
return FALSE;
} else {
/* one is null and the other isn't, they aren't equal */
return FALSE;
val1 = fmgr(typoutput, attr1,
gettypelem(tupdesc->attrs[att - 1]->atttypid));
val2 = fmgr(typoutput, attr2,
gettypelem(tupdesc->attrs[att - 1]->atttypid));
/*
* now, val1 and val2 are ascii representations so we can use
* strcmp for comparison
*/
if (strcmp(val1, val2) != 0)
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--
* Routines to handle materialization nodes.
* Routines to handle materialization nodes.
*
* Copyright (c) 1994, Regents of the University of California
*
*
* 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
* ExecMaterial - generate a temporary relation
* ExecInitMaterial - initialize node and subnodes..
* ExecEndMaterial - shutdown node and subnodes
* ExecMaterial - generate a temporary relation
* ExecInitMaterial - initialize node and subnodes..
* ExecEndMaterial - shutdown node and subnodes
*
*/
#include "postgres.h"
@ -29,368 +29,373 @@
#include "access/heapam.h"
/* ----------------------------------------------------------------
* ExecMaterial
* ExecMaterial
*
* The first time this is called, ExecMaterial retrieves tuples
* this node's outer subplan and inserts them into a temporary
* relation. After this is done, a flag is set indicating that
* the subplan has been materialized. Once the relation is
* materialized, the first tuple is then returned. Successive
* calls to ExecMaterial return successive tuples from the temp
* relation.
* The first time this is called, ExecMaterial retrieves tuples
* this node's outer subplan and inserts them into a temporary
* relation. After this is done, a flag is set indicating that
* the subplan has been materialized. Once the relation is
* materialized, the first tuple is then returned. Successive
* calls to ExecMaterial return successive tuples from the temp
* relation.
*
* Initial State:
* Initial State:
*
* ExecMaterial assumes the temporary relation has been
* created and openend by ExecInitMaterial during the prior
* InitPlan() phase.
* ExecMaterial assumes the temporary relation has been
* created and openend by ExecInitMaterial during the prior
* InitPlan() phase.
*
* ----------------------------------------------------------------
*/
TupleTableSlot * /* result tuple from subplan */
ExecMaterial(Material *node)
TupleTableSlot * /* result tuple from subplan */
ExecMaterial(Material * node)
{
EState *estate;
MaterialState *matstate;
Plan *outerNode;
ScanDirection dir;
Relation tempRelation;
Relation currentRelation;
HeapScanDesc currentScanDesc;
HeapTuple heapTuple;
TupleTableSlot *slot;
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
* 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;
EState *estate;
MaterialState *matstate;
Plan *outerNode;
ScanDirection dir;
Relation tempRelation;
Relation currentRelation;
HeapScanDesc currentScanDesc;
HeapTuple heapTuple;
TupleTableSlot *slot;
Buffer buffer;
ExecAssignScanType(&matstate->csstate,
RelationGetTupleDescriptor(currentRelation));
/* ----------------
* finally set the sorted flag to true
* get state info from node
* ----------------
*/
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 */
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
* 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;
}
/* ----------------
* 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 */
ExecInitMaterial(Material *node, EState *estate, Plan *parent)
bool /* initialization status */
ExecInitMaterial(Material * node, EState * estate, Plan * parent)
{
MaterialState *matstate;
Plan *outerPlan;
TupleDesc tupType;
Relation tempDesc;
/* int len; */
/* ----------------
* assign the node's execution state
* ----------------
*/
node->plan.state = estate;
/* ----------------
* create state structure
* ----------------
*/
matstate = makeNode(MaterialState);
matstate->mat_Flag = false;
matstate->mat_TempRelation = NULL;
node->matstate = matstate;
/* ----------------
* Miscellanious initialization
*
* + 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);
MaterialState *matstate;
Plan *outerPlan;
TupleDesc tupType;
Relation tempDesc;
/* int len; */
/* ----------------
* assign the node's execution state
* ----------------
*/
node->plan.state = estate;
/* ----------------
* create state structure
* ----------------
*/
matstate = makeNode(MaterialState);
matstate->mat_Flag = false;
matstate->mat_TempRelation = NULL;
node->matstate = matstate;
/* ----------------
* Miscellanious initialization
*
* + 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
/* ----------------
* tuple table initialization
* ----------------
*/
ExecInitScanTupleSlot(estate, &matstate->csstate);
/* ----------------
* initializes child nodes
* ----------------
*/
outerPlan = outerPlan((Plan *) node);
ExecInitNode(outerPlan, estate, (Plan *) node);
/* ----------------
* initialize matstate information
* ----------------
*/
matstate->mat_Flag = false;
/* ----------------
* initialize tuple type. no need to initialize projection
* info because this node doesn't do projections.
* ----------------
*/
ExecAssignScanTypeFromOuterPlan((Plan *) node, &matstate->csstate);
matstate->csstate.cstate.cs_ProjInfo = NULL;
/* ----------------
* get type information needed for ExecCreatR
* ----------------
*/
tupType = ExecGetScanType(&matstate->csstate);
/* ----------------
* ExecCreatR wants it's second argument to be an object id of
* a relation in the range table or a _TEMP_RELATION_ID
* indicating that the relation is not in the range table.
*
* In the second case ExecCreatR creates a temp relation.
* (currently this is the only case we support -cim 10/16/89)
* ----------------
*/
/* ----------------
* create the temporary relation
* ----------------
*/
/* len = ExecTargetListLength(node->plan.targetlist); */
tempDesc = ExecCreatR(tupType, _TEMP_RELATION_ID_);
/* ----------------
* save the relation descriptor in the sortstate
* ----------------
*/
matstate->mat_TempRelation = tempDesc;
matstate->csstate.css_currentRelation = tempDesc;
/* ----------------
* return relation oid of temporary relation in a list
* (someday -- for now we return LispTrue... cim 10/12/89)
* ----------------
*/
return TRUE;
/* ----------------
* tuple table initialization
* ----------------
*/
ExecInitScanTupleSlot(estate, &matstate->csstate);
/* ----------------
* initializes child nodes
* ----------------
*/
outerPlan = outerPlan((Plan *) node);
ExecInitNode(outerPlan, estate, (Plan *) node);
/* ----------------
* initialize matstate information
* ----------------
*/
matstate->mat_Flag = false;
/* ----------------
* initialize tuple type. no need to initialize projection
* info because this node doesn't do projections.
* ----------------
*/
ExecAssignScanTypeFromOuterPlan((Plan *) node, &matstate->csstate);
matstate->csstate.cstate.cs_ProjInfo = NULL;
/* ----------------
* get type information needed for ExecCreatR
* ----------------
*/
tupType = ExecGetScanType(&matstate->csstate);
/* ----------------
* ExecCreatR wants it's second argument to be an object id of
* a relation in the range table or a _TEMP_RELATION_ID
* indicating that the relation is not in the range table.
*
* In the second case ExecCreatR creates a temp relation.
* (currently this is the only case we support -cim 10/16/89)
* ----------------
*/
/* ----------------
* create the temporary relation
* ----------------
*/
/* len = ExecTargetListLength(node->plan.targetlist); */
tempDesc = ExecCreatR(tupType, _TEMP_RELATION_ID_);
/* ----------------
* save the relation descriptor in the sortstate
* ----------------
*/
matstate->mat_TempRelation = tempDesc;
matstate->csstate.css_currentRelation = tempDesc;
/* ----------------
* return relation oid of temporary relation in a list
* (someday -- for now we return LispTrue... cim 10/12/89)
* ----------------
*/
return TRUE;
}
int
ExecCountSlotsMaterial(Material *node)
ExecCountSlotsMaterial(Material * node)
{
return ExecCountSlotsNode(outerPlan((Plan *)node)) +
ExecCountSlotsNode(innerPlan((Plan *)node)) +
MATERIAL_NSLOTS;
return ExecCountSlotsNode(outerPlan((Plan *) node)) +
ExecCountSlotsNode(innerPlan((Plan *) node)) +
MATERIAL_NSLOTS;
}
/* ----------------------------------------------------------------
* ExecEndMaterial
* ExecEndMaterial
*
* old comments
* destroys the temporary relation.
* destroys the temporary relation.
* ----------------------------------------------------------------
*/
void
ExecEndMaterial(Material *node)
ExecEndMaterial(Material * node)
{
MaterialState *matstate;
Relation tempRelation;
Plan *outerPlan;
/* ----------------
* get info from the material state
* ----------------
*/
matstate = node->matstate;
tempRelation = matstate->mat_TempRelation;
heap_destroyr(tempRelation);
/* ----------------
* close the temp relation and shut down the scan.
* ----------------
*/
ExecCloseR((Plan *) node);
/* ----------------
* shut down the subplan
* ----------------
*/
outerPlan = outerPlan((Plan *) node);
ExecEndNode(outerPlan, (Plan*) node);
/* ----------------
* clean out the tuple table
* ----------------
*/
ExecClearTuple(matstate->csstate.css_ScanTupleSlot);
}
MaterialState *matstate;
Relation tempRelation;
Plan *outerPlan;
#ifdef NOT_USED /* not used */
/* ----------------
* get info from the material state
* ----------------
*/
matstate = node->matstate;
tempRelation = matstate->mat_TempRelation;
heap_destroyr(tempRelation);
/* ----------------
* close the temp relation and shut down the scan.
* ----------------
*/
ExecCloseR((Plan *) node);
/* ----------------
* shut down the subplan
* ----------------
*/
outerPlan = outerPlan((Plan *) node);
ExecEndNode(outerPlan, (Plan *) node);
/* ----------------
* clean out the tuple table
* ----------------
*/
ExecClearTuple(matstate->csstate.css_ScanTupleSlot);
}
#ifdef NOT_USED /* not used */
/* ----------------------------------------------------------------
* ExecMaterialMarkPos
* ExecMaterialMarkPos
* ----------------------------------------------------------------
*/
List /* nothing of interest */
List /* nothing of interest */
ExecMaterialMarkPos(Material node)
{
MaterialState matstate;
HeapScanDesc sdesc;
/* ----------------
* if we haven't materialized yet, just return NIL.
* ----------------
*/
matstate = get_matstate(node);
if (get_mat_Flag(matstate) == false)
MaterialState matstate;
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);
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
ExecMaterialRestrPos(Material node)
{
MaterialState matstate;
HeapScanDesc sdesc;
/* ----------------
* if we haven't materialized yet, just return.
* ----------------
*/
matstate = get_matstate(node);
if (get_mat_Flag(matstate) == false)
return;
/* ----------------
* restore the scan to the previously marked position
* ----------------
*/
sdesc = get_css_currentScanDesc((CommonScanState)matstate);
heap_restrpos(sdesc);
}
#endif
MaterialState matstate;
HeapScanDesc sdesc;
/* ----------------
* if we haven't materialized yet, just return.
* ----------------
*/
matstate = get_matstate(node);
if (get_mat_Flag(matstate) == false)
return;
/* ----------------
* restore the scan to the previously marked position
* ----------------
*/
sdesc = get_css_currentScanDesc((CommonScanState) matstate);
heap_restrpos(sdesc);
}
#endif

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