1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-28 23:42:10 +03:00

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

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

View File

@ -8,8 +8,8 @@
* For example array_int4eq returns true if some of the elements
* 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;
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]);
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");
@ -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, \
@ -57,37 +62,37 @@ int main(int argc, char **argv)
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++;
}
@ -95,4 +100,3 @@ bpchar %s\nvarchar %s\ntext %s\nabstime %s",
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];
char query[4000];
int row = 0;
int count;
char line[4000];
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 words");
on_error_stop();
@ -35,12 +36,12 @@ 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 \
@ -54,14 +55,14 @@ int main(int argc, char **argv)
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,109 +36,123 @@
* 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);
}
/*
@ -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;
bool isnull;
Datum atp;
int test;
for (; nkeys--; keys++) {
atp = (Datum)heap_getattr(t, InvalidBuffer,
keys->sk_attno,
tupdesc,
&isnull);
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 (isnull)
/* XXX eventually should check if SK_ISNULL */
return false;
if (keys->sk_flags & 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;
if (!ItemIdIsUsed(itemId))
return NULL;
tuple = (HeapTuple) PageGetItem((Page) disk_page, itemId);
tuple = (HeapTuple) PageGetItem((Page) disk_page, itemId);
if (key != NULL)
res = heap_keytest(tuple, RelationGetTupleDescriptor(relation),
nKeys, key);
else
res = TRUE;
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;
}
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;
if (TransactionIdEquals(t->t_xmax,
GetCurrentTransactionId()) &&
CommandIdGEScanCommandId(t->t_cmax))
return true;
return false;
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;
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);
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;
}
for (i = 0; i < numberOfAttributes && !hasnull; i++)
{
if (null[i] != ' ')
hasnull = true;
}
if (hasnull) infomask |= INDEX_NULL_MASK;
if (hasnull)
infomask |= INDEX_NULL_MASK;
hoff = IndexInfoFindDataOffset(infomask);
size = hoff
+ ComputeDataSize(tupleDescriptor,
value, null);
size = DOUBLEALIGN(size); /* be conservative */
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);
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));
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.
*/
/*
* 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;
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.
*/
/*
* 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);
if (size & 0xE000)
elog(WARN, "index_formtuple: data takes %d bytes: too big", size);
infomask |= size;
infomask |= size;
/* ----------------
* initialize metadata
* ----------------
*/
tuple->t_info = infomask;
return (tuple);
/* ----------------
* 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;
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;
/* ----------------
* 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;
/* first attribute is always at position zero */
for (; i <= byte; i++) {
n = bp[i];
if (i < byte) {
/* check for nulls in any "earlier" bytes */
if ((~n) != 0) {
slow++;
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;
RetrieveIndexResult result;
Assert(ItemPointerIsValid(indexItemPointer));
Assert(ItemPointerIsValid(heapItemPointer));
Assert(ItemPointerIsValid(indexItemPointer));
Assert(ItemPointerIsValid(heapItemPointer));
result = (RetrieveIndexResult) palloc(sizeof *result);
result = (RetrieveIndexResult) palloc(sizeof *result);
result->index_iptr = *indexItemPointer;
result->heap_iptr = *heapItemPointer;
result->index_iptr = *indexItemPointer;
result->heap_iptr = *heapItemPointer;
return (result);
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))
return ((Size) sizeof(IndexTupleData));
else
{
Size size = sizeof(IndexTupleData);
if (t_info & INDEX_NULL_MASK) {
size += sizeof(IndexAttributeBitMapData);
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 size;
IndexTuple ret;
size = IndexTupleSize(source);
if (*target == NULL) {
*target = (IndexTuple) palloc(size);
}
size = IndexTupleSize(source);
if (*target == NULL)
{
*target = (IndexTuple) palloc(size);
}
ret = *target;
memmove((char*)ret, (char*)source, 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;
bool isNull;
Datum datum;
int test;
IncrIndexProcessed();
IncrIndexProcessed();
while (scanKeySize > 0) {
datum = index_getattr(tuple,
key[0].sk_attno,
tupdesc,
&isNull);
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 (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_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++;
}
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 $
*
*-------------------------------------------------------------------------
*/
@ -24,271 +24,296 @@
#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;
HeapTuple typeTuple;
typeTuple = SearchSysCacheTuple(TYPOID,
ObjectIdGetDatum(type),
0, 0, 0);
typeTuple = SearchSysCacheTuple(TYPOID,
ObjectIdGetDatum(type),
0, 0, 0);
if (HeapTupleIsValid(typeTuple))
return((Oid)
((TypeTupleForm) GETSTRUCT(typeTuple))->typoutput);
if (HeapTupleIsValid(typeTuple))
return ((Oid)
((TypeTupleForm) GETSTRUCT(typeTuple))->typoutput);
elog(WARN, "typtoout: Cache lookup of type %d failed", type);
return(InvalidOid);
elog(WARN, "typtoout: Cache lookup of type %d failed", type);
return (InvalidOid);
}
Oid
gettypelem(Oid type)
{
HeapTuple typeTuple;
HeapTuple typeTuple;
typeTuple = SearchSysCacheTuple(TYPOID,
ObjectIdGetDatum(type),
0,0,0);
typeTuple = SearchSysCacheTuple(TYPOID,
ObjectIdGetDatum(type),
0, 0, 0);
if (HeapTupleIsValid(typeTuple))
return((Oid)
((TypeTupleForm) GETSTRUCT(typeTuple))->typelem);
if (HeapTupleIsValid(typeTuple))
return ((Oid)
((TypeTupleForm) GETSTRUCT(typeTuple))->typelem);
elog(WARN, "typtoout: Cache lookup of type %d failed", type);
return(InvalidOid);
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;
int i,
j,
k;
char *outputstr,
*attr;
bool isnull;
Oid typoutput;
/* ----------------
* tell the frontend to expect new tuple data
* ----------------
*/
pq_putnchar("D", 1);
/* ----------------
* 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;
/* ----------------
* 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);
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);
/* ----------------
* 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 (!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;
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);
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);
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;
int i,
j,
k;
char *attr;
bool isnull;
/* ----------------
* tell the frontend to expect new tuple data
* ----------------
*/
pq_putnchar("B", 1);
/* ----------------
* 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
/* ----------------
* 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));
Assert(PointerIsValid(entry));
entry->sk_flags = 0; /* just in case... */
entry->sk_attno = InvalidAttrNumber;
entry->sk_procedure = 0; /* should be InvalidRegProcedure */
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));
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);
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(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;
uint32 size;
TupleDesc desc;
/* ----------------
* sanity checks
* ----------------
*/
AssertArg(natts >= 1);
/* ----------------
* 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);
/* ----------------
* 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;
desc->natts = natts;
return (desc);
return (desc);
}
/* ----------------------------------------------------------------
* CreateTupleDesc
* CreateTupleDesc
*
* This function allocates a new TupleDesc from AttributeTupleForm array
* This function allocates a new TupleDesc from AttributeTupleForm array
* ----------------------------------------------------------------
*/
TupleDesc
CreateTupleDesc(int natts, AttributeTupleForm* attrs)
CreateTupleDesc(int natts, AttributeTupleForm * attrs)
{
TupleDesc desc;
TupleDesc desc;
/* ----------------
* sanity checks
* ----------------
*/
AssertArg(natts >= 1);
/* ----------------
* sanity checks
* ----------------
*/
AssertArg(natts >= 1);
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
desc->attrs = attrs;
desc->natts = natts;
desc->constr = NULL;
desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
desc->attrs = attrs;
desc->natts = natts;
desc->constr = NULL;
return (desc);
return (desc);
}
/* ----------------------------------------------------------------
* CreateTupleDescCopy
* CreateTupleDescCopy
*
* This function creates a new TupleDesc by copying from an existing
* TupleDesc
* This function creates a new TupleDesc by copying from an existing
* TupleDesc
*
* !!! Constraints are not copied !!!
* !!! 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;
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;
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));
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;
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_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);
}
}
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;
desc->constr = cpy;
}
else
desc->constr = NULL;
return desc;
return desc;
}
void
FreeTupleDesc (TupleDesc tupdesc)
FreeTupleDesc(TupleDesc tupdesc)
{
int i;
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 = 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_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);
}
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);
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;
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]));
/* ----------------
* 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;
}
AssertArg(PointerIsValid(desc));
AssertArg(attributeNumber >= 1);
/* ----------------
* type info exists so we initialize our attribute
* information from the type tuple we found..
* ----------------
*/
typeForm = (TypeTupleForm) GETSTRUCT(tuple);
/*
* attributeName's are sometimes NULL, from resdom's. I don't know
* why that is, though -- Jolly
*/
/* AssertArg(NameIsValid(attributeName));*/
/* AssertArg(NameIsValid(typeName));*/
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;
}
AssertArg(!PointerIsValid(desc->attrs[attributeNumber - 1]));
return true;
/* ----------------
* allocate storage for this attribute
* ----------------
*/
att = (AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE);
desc->attrs[attributeNumber - 1] = att;
/* ----------------
* initialize some of the attribute fields
* ----------------
*/
att->attrelid = 0; /* dummy value */
if (attributeName != NULL)
namestrcpy(&(att->attname), attributeName);
else
memset(att->attname.data, 0, NAMEDATALEN);
att->attdisbursion = 0; /* dummy value */
att->attcacheoff = -1;
att->attnum = attributeNumber;
att->attnelems = attdim;
att->attisset = attisset;
att->attnotnull = false;
att->atthasdef = false;
/* ----------------
* search the system cache for the type tuple of the attribute
* we are creating so that we can get the typeid and some other
* stuff.
*
* Note: in the special case of
*
* create EMP (name = char16, manager = EMP)
*
* RelationNameCreateHeapRelation() calls BuildDesc() which
* calls this routine and since EMP does not exist yet, the
* system cache lookup below fails. That's fine, but rather
* then doing a elog(WARN) we just leave that information
* uninitialized, return false, then fix things up later.
* -cim 6/14/90
* ----------------
*/
tuple = SearchSysCacheTuple(TYPNAME, PointerGetDatum(typeName),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
{
/* ----------------
* here type info does not exist yet so we just fill
* the attribute with dummy information and return false.
* ----------------
*/
att->atttypid = InvalidOid;
att->attlen = (int16) 0;
att->attbyval = (bool) 0;
att->attalign = 'i';
return false;
}
/* ----------------
* type info exists so we initialize our attribute
* information from the type tuple we found..
* ----------------
*/
typeForm = (TypeTupleForm) GETSTRUCT(tuple);
att->atttypid = tuple->t_oid;
att->attalign = typeForm->typalign;
/* ------------------------
If this attribute is a set, what is really stored in the
attribute is the OID of a tuple in the pg_proc catalog.
The pg_proc tuple contains the query string which defines
this set - i.e., the query to run to get the set.
So the atttypid (just assigned above) refers to the type returned
by this query, but the actual length of this attribute is the
length (size) of an OID.
Why not just make the atttypid point to the OID type, instead
of the type the query returns? Because the executor uses the atttypid
to tell the front end what type will be returned (in BeginCommand),
and in the end the type returned will be the result of the query, not
an OID.
Why not wait until the return type of the set is known (i.e., the
recursive call to the executor to execute the set has returned)
before telling the front end what the return type will be? Because
the executor is a delicate thing, and making sure that the correct
order of front-end commands is maintained is messy, especially
considering that target lists may change as inherited attributes
are considered, etc. Ugh.
-----------------------------------------
*/
if (attisset)
{
Type t = type("oid");
att->attlen = tlen(t);
att->attbyval = tbyval(t);
}
else
{
att->attlen = typeForm->typlen;
att->attbyval = typeForm->typbyval;
}
return true;
}
/* ----------------------------------------------------------------
* TupleDescMakeSelfReference
* TupleDescMakeSelfReference
*
* This function initializes a "self-referential" attribute like
* 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");
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;
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++;
natts = length(schema);
desc = CreateTemplateTupleDesc(natts);
constr->has_not_null = false;
entry = lfirst(p);
attname = entry->colname;
arry = entry->typename->arrayBounds;
attisset = entry->typename->setof;
attnum = 0;
strNcpy(typename, entry->typename->name,NAMEDATALEN-1);
if (arry != NIL)
attdim = length(arry);
else
attdim = 0;
typename = palloc(NAMEDATALEN);
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 )
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;
RetrieveIndexResult res;
/* if we have it cached in the scan desc, just return the value */
if ((res = gistscancache(s, dir)) != (RetrieveIndexResult) NULL)
/* 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);
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) {
while (n < FirstOffsetNumber || n > maxoff)
{
ReleaseBuffer(b);
if (so->s_stack == (GISTSTACK *) NULL)
return ((RetrieveIndexResult) NULL);
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);
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);
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);
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;
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));
blk = ItemPointerGetBlockNumber(&(s->currentItemData));
n = ItemPointerGetOffsetNumber(&(s->currentItemData));
if (ScanDirectionIsForward(dir)) {
n = OffsetNumberNext(n);
} 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 (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);
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 (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 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 (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;
RetrieveIndexResult res;
ItemPointer ip;
if (!(ScanDirectionIsNoMovement(dir)
&& ItemPointerIsValid(&(s->currentItemData)))) {
if (!(ScanDirectionIsNoMovement(dir)
&& ItemPointerIsValid(&(s->currentItemData))))
{
return ((RetrieveIndexResult) NULL);
}
return ((RetrieveIndexResult) NULL);
}
ip = gistheapptr(s->relation, &(s->currentItemData));
ip = gistheapptr(s->relation, &(s->currentItemData));
if (ItemPointerIsValid(ip))
res = FormRetrieveIndexResult(&(s->currentItemData), ip);
else
res = (RetrieveIndexResult) NULL;
if (ItemPointerIsValid(ip))
res = FormRetrieveIndexResult(&(s->currentItemData), ip);
else
res = (RetrieveIndexResult) NULL;
pfree (ip);
pfree(ip);
return (res);
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;
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);
}
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);
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
*
*-------------------------------------------------------------------------
*/
@ -21,372 +21,408 @@
#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;
IndexScanDesc s;
RelationSetLockForRead(r);
s = RelationGetIndexScan(r, fromEnd, nkeys, key);
gistregscan(s);
RelationSetLockForRead(r);
s = RelationGetIndexScan(r, fromEnd, nkeys, key);
gistregscan(s);
return (s);
return (s);
}
void
gistrescan(IndexScanDesc s, bool fromEnd, ScanKey key)
{
GISTScanOpaque p;
int i;
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++)
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;
}
} 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);
/*
* 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)
/*
** 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;
}
}
{
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;
}
}
}
void
gistmarkpos(IndexScanDesc s)
{
GISTScanOpaque p;
GISTSTACK *o, *n, *tmp;
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;
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;
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;
}
/* 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;
gistfreestack(p->s_markstk);
p->s_markstk = o;
}
void
gistrestrpos(IndexScanDesc s)
{
GISTScanOpaque p;
GISTSTACK *o, *n, *tmp;
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;
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;
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;
}
/* 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;
gistfreestack(p->s_stack);
p->s_stack = o;
}
void
gistendscan(IndexScanDesc s)
{
GISTScanOpaque p;
GISTScanOpaque p;
p = (GISTScanOpaque) s->opaque;
p = (GISTScanOpaque) s->opaque;
if (p != (GISTScanOpaque) NULL) {
gistfreestack(p->s_stack);
gistfreestack(p->s_markstk);
pfree (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 */
gistdropscan(s);
/* XXX don't unset read lock -- two-phase locking */
}
static void
gistregscan(IndexScanDesc s)
{
GISTScanList l;
GISTScanList l;
l = (GISTScanList) palloc(sizeof(GISTScanListData));
l->gsl_scan = s;
l->gsl_next = GISTScans;
GISTScans = 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;
GISTScanList l;
GISTScanList prev;
prev = (GISTScanList) NULL;
prev = (GISTScanList) NULL;
for (l = GISTScans;
l != (GISTScanList) NULL && l->gsl_scan != s;
l = l->gsl_next) {
prev = l;
}
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 (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;
if (prev == (GISTScanList) NULL)
GISTScans = l->gsl_next;
else
prev->gsl_next = l->gsl_next;
pfree(l);
pfree(l);
}
void
gistadjscans(Relation r, int op, BlockNumber blkno, OffsetNumber offnum)
{
GISTScanList l;
Oid relid;
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);
}
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;
GISTScanOpaque so;
adjustiptr(s, &(s->currentItemData), op, blkno, offnum);
adjustiptr(s, &(s->currentMarkData), op, blkno, offnum);
adjustiptr(s, &(s->currentItemData), op, blkno, offnum);
adjustiptr(s, &(s->currentMarkData), op, blkno, offnum);
so = (GISTScanOpaque) s->opaque;
so = (GISTScanOpaque) s->opaque;
if (op == GISTOP_SPLIT) {
adjuststack(so->s_stack, blkno, offnum);
adjuststack(so->s_markstk, blkno, offnum);
}
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;
OffsetNumber curoff;
GISTScanOpaque so;
if (ItemPointerIsValid(iptr)) {
if (ItemPointerGetBlockNumber(iptr) == blkno) {
curoff = ItemPointerGetOffsetNumber(iptr);
so = (GISTScanOpaque) s->opaque;
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) {
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;
}
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;
while (stk != (GISTSTACK *) NULL)
{
if (stk->gs_blk == blkno)
stk->gs_child = FirstOffsetNumber;
stk = stk->gs_parent;
}
stk = stk->gs_parent;
}
}

View File

@ -1,13 +1,13 @@
/*-------------------------------------------------------------------------
*
* giststrat.c--
* 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
*
*-------------------------------------------------------------------------
*/
@ -18,51 +18,51 @@
#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
@ -71,46 +71,47 @@ static StrategyNumber GISTNegateCommute[GISTNStrategies] = {
*/
/*
* 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;
Oid hrelid,
irelid;
Node *pred,
*oldPred;
/* note that this is a new btree */
BuildingHash = true;
/* note that this is a new btree */
BuildingHash = true;
pred = predInfo->pred;
oldPred = predInfo->oldPred;
pred = predInfo->pred;
oldPred = predInfo->oldPred;
/* initialize the hash index metadata page (if this is a new index) */
if (oldPred == NULL)
_hash_metapinit(index);
/* 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 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));
/* 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
*/
/*
* 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 */
#endif /* OMIT_PARTIAL_INDEX */
/* start a heap scan */
hscan = heap_beginscan(heap, 0, NowTimeQual, 0, (ScanKey) NULL);
htup = heap_getnext(hscan, 0, &buffer);
/* 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;
/* 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())
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);
/* okay, all heap tuples are indexed */
heap_endscan(hscan);
/* all done */
BuildingHash = false;
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;
/* 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);
if (itup->t_info & INDEX_NULL_MASK)
return ((InsertIndexResult) NULL);
hitem = _hash_formitem(itup);
hitem = _hash_formitem(itup);
res = _hash_doinsert(rel, hitem);
res = _hash_doinsert(rel, hitem);
pfree(hitem);
pfree(itup);
pfree(hitem);
pfree(itup);
return (res);
return (res);
}
/*
* hashgettuple() -- Get the next tuple in the scan.
* hashgettuple() -- Get the next tuple in the scan.
*/
char *
char *
hashgettuple(IndexScanDesc scan, ScanDirection dir)
{
RetrieveIndexResult 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 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);
if (ItemPointerIsValid(&(scan->currentItemData)))
res = _hash_next(scan, dir);
else
res = _hash_first(scan, dir);
return ((char *) res);
return ((char *) res);
}
/*
* hashbeginscan() -- start a scan on a hash index
* hashbeginscan() -- start a scan on a hash index
*/
char *
char *
hashbeginscan(Relation rel,
bool fromEnd,
uint16 keysz,
ScanKey scankey)
bool fromEnd,
uint16 keysz,
ScanKey scankey)
{
IndexScanDesc scan;
HashScanOpaque so;
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;
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);
/* register scan in case we change pages it's using */
_hash_regscan(scan);
return ((char *) 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;
ItemPointer iptr;
HashScanOpaque so;
so = (HashScanOpaque) scan->opaque;
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);
}
/* 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));
}
/* 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;
ItemPointer iptr;
HashScanOpaque so;
so = (HashScanOpaque) scan->opaque;
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);
}
/* 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);
}
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);
/* don't need scan registered anymore */
_hash_dropscan(scan);
/* be tidy */
pfree (scan->opaque);
/* be tidy */
pfree(scan->opaque);
}
/*
* hashmarkpos() -- save current scan position
* hashmarkpos() -- save current scan position
*
*/
void
hashmarkpos(IndexScanDesc scan)
{
ItemPointer iptr;
HashScanOpaque so;
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.");
/*
* 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;
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);
}
/* 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;
}
/* 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;
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.");
/*
* 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;
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);
}
/* 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);
/* 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;
}
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);
/* 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);
/* 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;
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);
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;
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);
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 hashoid(Oid key)
uint32
hashoid(Oid key)
{
return ((uint32) ~key);
return ((uint32) ~ key);
}
uint32 hashchar(char key)
uint32
hashchar(char key)
{
int len;
uint32 h;
int len;
uint32 h;
len = sizeof(char);
len = sizeof(char);
#define PRIME1 37
#define PRIME2 1048583
#define PRIME1 37
#define PRIME2 1048583
h = 0;
/* Convert char to integer */
h = h * PRIME1 ^ (key - ' ');
h %= PRIME2;
h = 0;
/* Convert char to integer */
h = h * PRIME1 ^ (key - ' ');
h %= PRIME2;
return (h);
return (h);
}
uint32 hashchar2(uint16 intkey)
uint32
hashchar2(uint16 intkey)
{
uint32 h;
int len;
char *key = (char *) &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;
h = 0;
len = sizeof(uint16);
/* Convert string to integer */
while (len--)
h = h * PRIME1 ^ (*key++ - ' ');
h %= PRIME2;
return (h);
return (h);
}
uint32 hashchar4(uint32 intkey)
uint32
hashchar4(uint32 intkey)
{
uint32 h;
int len;
char *key = (char *) &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;
h = 0;
len = sizeof(uint32);
/* Convert string to integer */
while (len--)
h = h * PRIME1 ^ (*key++ - ' ');
h %= PRIME2;
return (h);
return (h);
}
uint32 hashchar8(char *key)
uint32
hashchar8(char *key)
{
uint32 h;
int len;
uint32 h;
int len;
h = 0;
len = sizeof(char8);
/* Convert string to integer */
while (len--)
h = h * PRIME1 ^ (*key++ - ' ');
h %= PRIME2;
h = 0;
len = sizeof(char8);
/* Convert string to integer */
while (len--)
h = h * PRIME1 ^ (*key++ - ' ');
h %= PRIME2;
return (h);
return (h);
}
uint32 hashname(NameData *n)
uint32
hashname(NameData * n)
{
uint32 h;
int len;
char *key;
uint32 h;
int len;
char *key;
key = n->data;
key = n->data;
h = 0;
len = NAMEDATALEN;
/* Convert string to integer */
while (len--)
h = h * PRIME1 ^ (*key++ - ' ');
h %= PRIME2;
h = 0;
len = NAMEDATALEN;
/* Convert string to integer */
while (len--)
h = h * PRIME1 ^ (*key++ - ' ');
h %= PRIME2;
return (h);
return (h);
}
uint32 hashchar16(char *key)
uint32
hashchar16(char *key)
{
uint32 h;
int len;
uint32 h;
int len;
h = 0;
len = sizeof(char16);
/* Convert string to integer */
while (len--)
h = h * PRIME1 ^ (*key++ - ' ');
h %= PRIME2;
h = 0;
len = sizeof(char16);
/* Convert string to integer */
while (len--)
h = h * PRIME1 ^ (*key++ - ' ');
h %= PRIME2;
return (h);
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;
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);
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,13 +1,13 @@
/*-------------------------------------------------------------------------
*
* 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 $
*
*-------------------------------------------------------------------------
*/
@ -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;
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);
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);
/* 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);
/*
* 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);
/*
* 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.
*/
/*
* 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);
/* do the insertion */
res = _hash_insertonpg(rel, buf, natts, itup_scankey,
hitem, metabuf);
/* be tidy */
_hash_freeskey(itup_scankey);
/* be tidy */
_hash_freeskey(itup_scankey);
return (res);
return (res);
}
/*
* _hash_insertonpg() -- Insert a tuple on a particular page in the table.
* _hash_insertonpg() -- Insert a tuple on a particular page in the table.
*
* This recursive procedure does the following things:
* This recursive procedure does the following things:
*
* + if necessary, splits the target page.
* + 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;
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);
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;
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);
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);
itemsz = IndexTupleDSize(hitem->hash_itup)
+ (sizeof(HashItemData) - sizeof(IndexTupleData));
itemsz = DOUBLEALIGN(itemsz);
/* by here, the new tuple is inserted */
res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData));
while (PageGetFreeSpace(page) < itemsz)
{
ItemPointerSet(&(res->pointerData), itup_blkno, itup_off);
/*
* no space on this page; check for an overflow page
*/
if (BlockNumberIsValid(pageopaque->hasho_nextblkno))
{
if (res != NULL) {
/*
* 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);
/*
* 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);
_hash_wrtbuf(rel, buf);
if (PageGetFreeSpace(page) < itemsz)
{
/* it doesn't fit on an empty page -- give up */
elog(WARN, "hash item too large");
}
}
_hash_checkpage(page, LH_OVERFLOW_PAGE);
pageopaque = (HashPageOpaque) PageGetSpecialPointer(page);
Assert(pageopaque->hasho_bucket == bucket);
}
if (do_expand ||
(metap->hashm_nkeys / (metap->hashm_maxbucket + 1))
> metap->hashm_ffactor) {
_hash_expandtable(rel, metabuf);
}
_hash_relbuf(rel, metabuf, HASH_READ);
return (res);
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;
OffsetNumber itup_off;
Page page;
page = BufferGetPage(buf);
_hash_checkpage(page, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE);
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);
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);
/* write the buffer, but hold our lock */
_hash_wrtnorelbuf(rel, buf);
return (itup_off);
return (itup_off);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,28 +1,28 @@
/*-------------------------------------------------------------------------
*
* hashscan.c--
* 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.
*
*-------------------------------------------------------------------------
*/
@ -31,130 +31,137 @@
#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;
HashScanList new_el;
new_el = (HashScanList) palloc(sizeof(HashScanListData));
new_el->hashsl_scan = scan;
new_el->hashsl_next = HashScans;
HashScans = 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;
HashScanList chk,
last;
last = (HashScanList) NULL;
for (chk = HashScans;
chk != (HashScanList) NULL && chk->hashsl_scan != scan;
chk = chk->hashsl_next) {
last = chk;
}
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 (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;
if (last == (HashScanList) NULL)
HashScans = chk->hashsl_next;
else
last->hashsl_next = chk->hashsl_next;
pfree (chk);
pfree(chk);
}
void
_hash_adjscans(Relation rel, ItemPointer tid)
{
HashScanList l;
Oid relid;
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));
}
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;
ItemPointer current;
Buffer buf;
Buffer metabuf;
HashScanOpaque so;
if (!_hash_scantouched(scan, blkno, offno))
return;
if (!_hash_scantouched(scan, blkno, offno))
return;
metabuf = _hash_getbuf(scan->relation, HASH_METAPAGE, HASH_READ);
metabuf = _hash_getbuf(scan->relation, HASH_METAPAGE, HASH_READ);
so = (HashScanOpaque) scan->opaque;
buf = so->hashso_curbuf;
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->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;
}
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;
ItemPointer current;
current = &(scan->currentItemData);
if (ItemPointerIsValid(current)
&& ItemPointerGetBlockNumber(current) == blkno
&& ItemPointerGetOffsetNumber(current) >= offno)
return (true);
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);
current = &(scan->currentMarkData);
if (ItemPointerIsValid(current)
&& ItemPointerGetBlockNumber(current) == blkno
&& ItemPointerGetOffsetNumber(current) >= offno)
return (true);
return (false);
return (false);
}

View File

@ -1,13 +1,13 @@
/*-------------------------------------------------------------------------
*
* 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 $
*
*-------------------------------------------------------------------------
*/
@ -18,406 +18,450 @@
#include <storage/bufmgr.h>
#ifndef HAVE_MEMMOVE
# include "regex/utils.h"
#include "regex/utils.h"
#else
# include <string.h>
#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);
/*
* If the scankey argument is NULL, all tuples will satisfy the
* scan so we start the scan at the first bucket (bucket 0).
*/
bucket = 0;
}
else
{
bucket = _hash_call(rel, metap, keyDatum);
}
*bufP = _hash_getbuf(rel, blkno, HASH_READ);
blkno = BUCKET_TO_BLKNO(bucket);
*bufP = _hash_getbuf(rel, blkno, HASH_READ);
}
/*
* _hash_next() -- Get the next item in a scan.
* _hash_next() -- Get the next item in a scan.
*
* On entry, we have a valid currentItemData in the scan, and a
* 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.
* 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.
* 'bufP' points to the buffer which contains the current page
* that we'll step through.
*
* 'metabuf' is released when this returns.
* '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);
}
/*
* '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;
/* get ready to check this tuple */
hitem = (HashItem) PageGetItem(page, PageGetItemId(page, offnum));
itup = &hitem->hash_itup;
} while (!_hash_checkqual(scan, itup));
switch (dir)
{
case ForwardScanDirection:
if (offnum != InvalidOffsetNumber)
{
offnum = OffsetNumberNext(offnum); /* move forward */
}
else
{
offnum = FirstOffsetNumber; /* new page */
}
while (offnum > maxoff)
{
/* if we made it to here, we've found a valid tuple */
_hash_relbuf(rel, metabuf, HASH_READ);
blkno = BufferGetBlockNumber(buf);
*bufP = so->hashso_curbuf = buf;
ItemPointerSet(current, blkno, offnum);
return(true);
/*
* 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,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/hash/Attic/hashstrat.c,v 1.9 1997/08/20 02:01:42 vadim Exp $
* $Header: /cvsroot/pgsql/src/backend/access/hash/Attic/hashstrat.c,v 1.10 1997/09/07 04:38:03 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -18,63 +18,66 @@
#include <access/istrat.h>
/*
* 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,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/hash/hashutil.c,v 1.9 1997/08/14 05:01:32 vadim Exp $
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashutil.c,v 1.10 1997/09/07 04:38:04 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -20,90 +20,91 @@
#include <access/iqual.h>
#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;
ScanKey skey;
TupleDesc itupdesc;
int natts;
AttrNumber i;
Datum arg;
RegProcedure proc;
bool null;
natts = rel->rd_rel->relnatts;
itupdesc = RelationGetTupleDescriptor(rel);
natts = rel->rd_rel->relnatts;
itupdesc = RelationGetTupleDescriptor(rel);
skey = (ScanKey) palloc(natts * sizeof(ScanKeyData));
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);
}
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);
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;
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");
/* 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));
/* 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);
hitem = (HashItem) palloc(nbytes_hitem);
memmove((char *) &(hitem->hash_itup), (char *) itup, tuplen);
return (hitem);
return (hitem);
}
Bucket
_hash_call(Relation rel, HashMetaPage metap, Datum key)
{
uint32 n;
Bucket bucket;
RegProcedure proc;
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);
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;
uint32 i,
limit;
limit = 1;
for (i = 0; limit < num; limit = limit << 1, i++)
;
return (i);
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;
Buffer buffer;
Page pageHeader;
BlockNumber numberOfBlocks;
OffsetNumber offnum;
unsigned int len;
ItemId itemId;
Item item;
/* ----------------
* increment access statistics
* ----------------
*/
IncrHeapAccessStat(local_RelationPutHeapTuple);
IncrHeapAccessStat(global_RelationPutHeapTuple);
/* ----------------
* increment access statistics
* ----------------
*/
IncrHeapAccessStat(local_RelationPutHeapTuple);
IncrHeapAccessStat(global_RelationPutHeapTuple);
Assert(RelationIsValid(relation));
Assert(HeapTupleIsValid(tuple));
Assert(RelationIsValid(relation));
Assert(HeapTupleIsValid(tuple));
numberOfBlocks = RelationGetNumberOfBlocks(relation);
Assert(blockIndex < numberOfBlocks);
numberOfBlocks = RelationGetNumberOfBlocks(relation);
Assert(blockIndex < numberOfBlocks);
buffer = ReadBuffer(relation, blockIndex);
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));
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);
offnum = PageAddItem((Page) pageHeader, (Item) tuple,
tuple->t_len, InvalidOffsetNumber, LP_USED);
itemId = PageGetItemId((Page)pageHeader, offnum);
item = PageGetItem((Page)pageHeader, itemId);
itemId = PageGetItemId((Page) pageHeader, offnum);
item = PageGetItem((Page) pageHeader, itemId);
ItemPointerSet(&((HeapTuple)item)->t_ctid, blockIndex, offnum);
ItemPointerSet(&((HeapTuple) item)->t_ctid, blockIndex, offnum);
WriteBuffer(buffer);
/* return an accurate tuple */
ItemPointerSet(&tuple->t_ctid, blockIndex, offnum);
WriteBuffer(buffer);
/* return an accurate tuple */
ItemPointerSet(&tuple->t_ctid, blockIndex, offnum);
}
/*
@ -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;
Buffer buffer;
Page pageHeader;
BlockNumber lastblock;
OffsetNumber offnum;
unsigned int len;
ItemId itemId;
Item item;
Assert(RelationIsValid(relation));
Assert(HeapTupleIsValid(tuple));
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.
*/
/*
* 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);
lastblock = RelationGetNumberOfBlocks(relation);
if (lastblock == 0)
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);
else
buffer = ReadBuffer(relation, lastblock - 1);
pageHeader = (Page)BufferGetPage(buffer);
len = (unsigned)DOUBLEALIGN(tuple->t_len); /* be conservative */
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.
*/
/*
* 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))
if (len > PageGetFreeSpace(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);
if (len > PageGetFreeSpace(pageHeader))
elog(WARN, "Tuple is too big: size %d", len);
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);
offnum = PageAddItem((Page) pageHeader, (Item) tuple,
tuple->t_len, InvalidOffsetNumber, LP_USED);
itemId = PageGetItemId((Page)pageHeader, offnum);
item = PageGetItem((Page)pageHeader, itemId);
itemId = PageGetItemId((Page) pageHeader, offnum);
item = PageGetItem((Page) pageHeader, itemId);
lastblock = BufferGetBlockNumber(buffer);
lastblock = BufferGetBlockNumber(buffer);
ItemPointerSet(&((HeapTuple)item)->t_ctid, lastblock, offnum);
ItemPointerSet(&((HeapTuple) item)->t_ctid, lastblock, offnum);
/* return an accurate tuple */
ItemPointerSet(&tuple->t_ctid, lastblock, offnum);
/* return an accurate tuple */
ItemPointerSet(&tuple->t_ctid, lastblock, offnum);
WriteBuffer(buffer);
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,15 +23,15 @@
#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;
@ -39,306 +39,311 @@ HeapAccessStatistics heap_access_stats = (HeapAccessStatistics) NULL;
static void
InitHeapAccessStatistics()
{
MemoryContext oldContext;
HeapAccessStatistics stats;
MemoryContext oldContext;
HeapAccessStatistics stats;
/* ----------------
* make sure we don't initialize things twice
* ----------------
*/
if (heap_access_stats != NULL)
return;
/* ----------------
* 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);
/* ----------------
* allocate statistics structure from the top memory context
* ----------------
*/
oldContext = MemoryContextSwitchTo(TopMemoryContext);
stats = (HeapAccessStatistics)
palloc(sizeof(HeapAccessStatisticsData));
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;
/* ----------------
* 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;
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);
/* ----------------
* 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);
/* ----------------
* return to old memory context
* ----------------
*/
MemoryContextSwitchTo(oldContext);
heap_access_stats = stats;
heap_access_stats = stats;
}
#ifdef NOT_USED
/* ----------------
* ResetHeapAccessStatistics
* ResetHeapAccessStatistics
* ----------------
*/
void
ResetHeapAccessStatistics()
{
HeapAccessStatistics stats;
HeapAccessStatistics stats;
/* ----------------
* do nothing if stats aren't initialized
* ----------------
*/
if (heap_access_stats == NULL)
return;
/* ----------------
* do nothing if stats aren't initialized
* ----------------
*/
if (heap_access_stats == NULL)
return;
stats = heap_access_stats;
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 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);
/* ----------------
* 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;
HeapAccessStatistics stats;
/* ----------------
* return nothing if stats aren't initialized
* ----------------
*/
if (heap_access_stats == NULL)
return NULL;
/* ----------------
* 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);
/* ----------------
* 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));
/* ----------------
* allocate a copy of the stats and return it to the caller.
* ----------------
*/
stats = (HeapAccessStatistics)
palloc(sizeof(HeapAccessStatisticsData));
memmove(stats,
heap_access_stats,
sizeof(HeapAccessStatisticsData));
memmove(stats,
heap_access_stats,
sizeof(HeapAccessStatisticsData));
return stats;
return stats;
}
#endif
#ifdef NOT_USED
/* ----------------
* PrintHeapAccessStatistics
* PrintHeapAccessStatistics
* ----------------
*/
void
PrintHeapAccessStatistics(HeapAccessStatistics stats)
{
/* ----------------
* return nothing if stats aren't valid
* ----------------
*/
if (stats == NULL)
return;
/* ----------------
* 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("======== 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("local_reset_timestamp: %s",
ctime(&(stats->local_reset_timestamp)));
printf("last_request_timestamp: %s",
ctime(&(stats->last_request_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_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_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_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_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_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_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_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_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_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_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_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_markpos: %6d/%6d\n",
stats->local_markpos, stats->global_markpos);
printf("local/global_restrpos: %6d/%6d\n",
stats->local_restrpos, stats->global_restrpos);
printf("local/global_restrpos: %6d/%6d\n",
stats->local_restrpos, stats->global_restrpos);
printf("================\n");
printf("================\n");
printf("local/global_BufferGetRelation: %6d/%6d\n",
stats->local_BufferGetRelation,
stats->global_BufferGetRelation);
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: %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_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_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_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_RelationPutHeapTuple: %6d/%6d\n",
stats->local_RelationPutHeapTuple,
stats->global_RelationPutHeapTuple);
printf("local/global_RelationPutLongHeapTuple: %6d/%6d\n",
stats->local_RelationPutLongHeapTuple,
stats->global_RelationPutLongHeapTuple);
printf("local/global_RelationPutLongHeapTuple: %6d/%6d\n",
stats->local_RelationPutLongHeapTuple,
stats->global_RelationPutLongHeapTuple);
printf("===================================\n");
printf("===================================\n");
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;
IndexScanDesc scan;
if (! RelationIsValid(relation))
elog(WARN, "RelationGetIndexScan: relation invalid");
if (!RelationIsValid(relation))
elog(WARN, "RelationGetIndexScan: relation invalid");
scan = (IndexScanDesc) palloc(sizeof(IndexScanDescData));
scan = (IndexScanDesc) palloc(sizeof(IndexScanDescData));
scan->relation = relation;
scan->opaque = NULL;
scan->numberOfKeys = numberOfKeys;
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);
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;
}
if (numberOfKeys > 0)
{
scan->keyData = (ScanKey) palloc(sizeof(ScanKeyData) * numberOfKeys);
}
else
{
scan->keyData = NULL;
}
index_rescan(scan, scanFromEnd, key);
index_rescan(scan, scanFromEnd, key);
return (scan);
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");
if (!IndexScanIsValid(scan))
elog(WARN, "IndexScanRestart: invalid scan");
ItemPointerSetInvalid(&scan->previousItemData);
ItemPointerSetInvalid(&scan->currentItemData);
ItemPointerSetInvalid(&scan->nextItemData);
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;
if (RelationGetNumberOfBlocks(scan->relation) == 0)
scan->flags = ScanUnmarked;
else if (scanFromEnd)
scan->flags = ScanUnmarked | ScanUncheckedPrevious;
else
scan->flags = ScanUnmarked | ScanUncheckedNext;
scan->scanFromEnd = (bool) scanFromEnd;
scan->scanFromEnd = (bool) scanFromEnd;
if (scan->numberOfKeys > 0)
memmove(scan->keyData,
key,
scan->numberOfKeys * sizeof(ScanKeyData));
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");
if (!IndexScanIsValid(scan))
elog(WARN, "IndexScanEnd: invalid scan");
pfree(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;
RetrieveIndexResult result;
if (scan->flags & ScanUncheckedPrevious) {
result =
index_getnext(scan, BackwardScanDirection);
if (scan->flags & ScanUncheckedPrevious)
{
result =
index_getnext(scan, BackwardScanDirection);
if (result != NULL) {
scan->previousItemData = result->index_iptr;
} else {
ItemPointerSetInvalid(&scan->previousItemData);
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);
scan->previousMarkData = scan->previousItemData;
scan->currentMarkData = scan->currentItemData;
scan->nextMarkData = scan->nextItemData;
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->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");
if (scan->flags & ScanUnmarked)
elog(WARN, "IndexScanRestorePosition: no mark to restore");
scan->previousItemData = scan->previousMarkData;
scan->currentItemData = scan->currentMarkData;
scan->nextItemData = scan->nextMarkData;
scan->previousItemData = scan->previousMarkData;
scan->currentItemData = scan->currentMarkData;
scan->nextItemData = scan->nextMarkData;
scan->flags = 0x0; /* XXX should have a symbolic name */
scan->flags = 0x0; /* XXX should have a symbolic name */
}

View File

@ -1,64 +1,64 @@
/*-------------------------------------------------------------------------
*
* indexam.c--
* 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
* index_getprocid - get a support procedure id from the rel tuple
*
* IndexScanIsValid - check index scan
* 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.
*
*-------------------------------------------------------------------------
*/
@ -72,9 +72,9 @@
#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;
RegProcedure procedure;
InsertIndexResult specificResult;
RELATION_CHECKS;
GET_REL_PROCEDURE(insert,aminsert);
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);
/* ----------------
* 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
* ----------------
*/
/* ----------------
* 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);
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;
RegProcedure procedure;
RELATION_CHECKS;
GET_REL_PROCEDURE(delete,amdelete);
RELATION_CHECKS;
GET_REL_PROCEDURE(delete, amdelete);
fmgr(procedure, relation, indexItem);
fmgr(procedure, relation, indexItem);
}
/* ----------------
* index_beginscan - start a scan of an index
* index_beginscan - start a scan of an index
* ----------------
*/
IndexScanDesc
index_beginscan(Relation relation,
bool scanFromEnd,
uint16 numberOfKeys,
ScanKey key)
bool scanFromEnd,
uint16 numberOfKeys,
ScanKey key)
{
IndexScanDesc scandesc;
RegProcedure procedure;
IndexScanDesc scandesc;
RegProcedure procedure;
RELATION_CHECKS;
GET_REL_PROCEDURE(beginscan,ambeginscan);
RELATION_CHECKS;
GET_REL_PROCEDURE(beginscan, ambeginscan);
RelationSetRIntentLock(relation);
RelationSetRIntentLock(relation);
scandesc = (IndexScanDesc)
fmgr(procedure, relation, scanFromEnd, numberOfKeys, key);
scandesc = (IndexScanDesc)
fmgr(procedure, relation, scanFromEnd, numberOfKeys, key);
return scandesc;
return scandesc;
}
/* ----------------
* index_rescan - restart a scan of an index
* index_rescan - restart a scan of an index
* ----------------
*/
void
index_rescan(IndexScanDesc scan, bool scanFromEnd, ScanKey key)
{
RegProcedure procedure;
RegProcedure procedure;
SCAN_CHECKS;
GET_SCAN_PROCEDURE(rescan,amrescan);
SCAN_CHECKS;
GET_SCAN_PROCEDURE(rescan, amrescan);
fmgr(procedure, scan, scanFromEnd, key);
fmgr(procedure, scan, scanFromEnd, key);
}
/* ----------------
* index_endscan - end a scan
* index_endscan - end a scan
* ----------------
*/
void
index_endscan(IndexScanDesc scan)
{
RegProcedure procedure;
RegProcedure procedure;
SCAN_CHECKS;
GET_SCAN_PROCEDURE(endscan,amendscan);
SCAN_CHECKS;
GET_SCAN_PROCEDURE(endscan, amendscan);
fmgr(procedure, scan);
fmgr(procedure, scan);
RelationUnsetRIntentLock(scan->relation);
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;
RegProcedure procedure;
SCAN_CHECKS;
GET_SCAN_PROCEDURE(markpos,ammarkpos);
SCAN_CHECKS;
GET_SCAN_PROCEDURE(markpos, ammarkpos);
fmgr(procedure, scan);
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;
RegProcedure procedure;
SCAN_CHECKS;
GET_SCAN_PROCEDURE(restrpos,amrestrpos);
SCAN_CHECKS;
GET_SCAN_PROCEDURE(restrpos, amrestrpos);
fmgr(procedure, scan);
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;
RegProcedure procedure;
RetrieveIndexResult result;
SCAN_CHECKS;
GET_SCAN_PROCEDURE(getnext,amgettuple);
SCAN_CHECKS;
GET_SCAN_PROCEDURE(getnext, amgettuple);
/* ----------------
* have the am's gettuple proc do all the work.
* ----------------
*/
result = (RetrieveIndexResult)
fmgr(procedure, scan, direction);
/* ----------------
* have the am's gettuple proc do all the work.
* ----------------
*/
result = (RetrieveIndexResult)
fmgr(procedure, scan, direction);
return result;
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;
RegProcedure *loc;
int natts;
natts = irel->rd_rel->relnatts;
natts = irel->rd_rel->relnatts;
loc = irel->rd_support;
loc = irel->rd_support;
Assert(loc != NULL);
Assert(loc != NULL);
return (loc[(natts * (procnum - 1)) + (attnum - 1)]);
return (loc[(natts * (procnum - 1)) + (attnum - 1)]);
}
Datum
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;
Datum returnVal;
bool isNull;
if (PointerIsValid(fInfo) && FIgetProcOid(fInfo) != InvalidOid) {
int i;
Datum *attData = (Datum *)palloc(FIgetnArgs(fInfo)*sizeof(Datum));
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);
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;
int la = VARSIZE(a) - VARHDRSZ;
int lb = VARSIZE(b) - VARHDRSZ;
ap = (unsigned char *) palloc (la + 1);
bp = (unsigned char *) palloc (lb + 1);
ap = (unsigned char *) palloc(la + 1);
bp = (unsigned char *) palloc(lb + 1);
memcpy(ap, VARDATA(a), la);
*(ap + la) = '\0';
memcpy(bp, VARDATA(b), lb);
*(bp + lb) = '\0';
memcpy(ap, VARDATA(a), la);
*(ap + la) = '\0';
memcpy(bp, VARDATA(b), lb);
*(bp + lb) = '\0';
res = strcoll (ap, bp);
res = strcoll(ap, bp);
pfree (ap);
pfree (bp);
pfree(ap);
pfree(bp);
#else
int len = VARSIZE(a);
int len = VARSIZE(a);
/* len is the length of the shorter of the two strings */
if ( len > VARSIZE(b) )
len = VARSIZE(b);
/* len is the length of the shorter of the two strings */
if (len > VARSIZE(b))
len = VARSIZE(b);
len -= VARHDRSZ;
len -= VARHDRSZ;
ap = (unsigned char *) VARDATA(a);
bp = (unsigned char *) VARDATA(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.
*/
/*
* 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);
}
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);
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.
*/
/*
* 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 (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;
BTScanList new_el;
new_el = (BTScanList) palloc(sizeof(BTScanListData));
new_el->btsl_scan = scan;
new_el->btsl_next = BTScans;
BTScans = 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;
BTScanList chk,
last;
last = (BTScanList) NULL;
for (chk = BTScans;
chk != (BTScanList) NULL && chk->btsl_scan != scan;
chk = chk->btsl_next) {
last = chk;
}
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 (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;
if (last == (BTScanList) NULL)
BTScans = chk->btsl_next;
else
last->btsl_next = chk->btsl_next;
pfree (chk);
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;
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));
}
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;
ItemPointer current;
Buffer buf;
BTScanOpaque so;
if (!_bt_scantouched(scan, blkno, offno))
return;
if (!_bt_scantouched(scan, blkno, offno))
return;
so = (BTScanOpaque) scan->opaque;
buf = so->btso_curbuf;
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*/
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;
ItemPointer current;
current = &(scan->currentItemData);
if (ItemPointerIsValid(current)
&& ItemPointerGetBlockNumber(current) == blkno
&& ItemPointerGetOffsetNumber(current) >= offno)
return (true);
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);
current = &(scan->currentMarkData);
if (ItemPointerIsValid(current)
&& ItemPointerGetBlockNumber(current) == blkno
&& ItemPointerGetOffsetNumber(current) >= offno)
return (true);
return (false);
return (false);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,13 @@
/*-------------------------------------------------------------------------
*
* btstrat.c--
* 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 */
static StrategyTerm BTEqualExpressionData[] = {
(StrategyTerm) BTLessTermData, /* XXX */
(StrategyTerm) BTLessEqualTermData, /* XXX */
(StrategyTerm) BTGreaterEqualTermData, /* XXX */
(StrategyTerm) BTGreaterTermData, /* XXX */
NULL
};
BTMaxStrategyNumber,
(StrategyTransformMap)BTNegate, /* XXX */
(StrategyTransformMap)BTCommute, /* XXX */
(StrategyTransformMap)BTNegateCommute, /* XXX */
static StrategyEvaluationData BTEvaluationData = {
/* XXX static for simplicity */
{ NULL, NULL, (StrategyExpression)BTEqualExpressionData, NULL, NULL,
NULL,NULL,NULL,NULL,NULL,NULL,NULL}
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;
StrategyNumber strat;
strat = RelationGetStrategy(rel, attno, &BTEvaluationData, proc);
strat = RelationGetStrategy(rel, attno, &BTEvaluationData, proc);
Assert(StrategyNumberIsValid(strat));
Assert(StrategyNumberIsValid(strat));
return (strat);
return (strat);
}
bool
_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
_bt_mkscankey(Relation rel, IndexTuple itup)
{
ScanKey skey;
TupleDesc itupdesc;
int natts;
int i;
Datum arg;
RegProcedure proc;
bool null;
bits16 flag;
ScanKey skey;
TupleDesc itupdesc;
int natts;
int i;
Datum arg;
RegProcedure proc;
bool null;
bits16 flag;
natts = rel->rd_rel->relnatts;
itupdesc = RelationGetTupleDescriptor(rel);
natts = rel->rd_rel->relnatts;
itupdesc = RelationGetTupleDescriptor(rel);
skey = (ScanKey) palloc(natts * sizeof(ScanKeyData));
skey = (ScanKey) palloc(natts * sizeof(ScanKeyData));
for (i = 0; i < natts; i++) {
arg = index_getattr(itup, i + 1, itupdesc, &null);
if ( null )
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;
BTStack ostack;
while (stack != (BTStack) NULL) {
ostack = stack;
stack = stack->bts_parent;
pfree(ostack->bts_btitem);
pfree(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;
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;
if (numberOfKeys < 1)
return;
key = so->keyData;
key = so->keyData;
cur = &key[0];
if ( cur->sk_attno != 1 )
elog (WARN, "_bt_orderkeys: key(s) for attribute 1 missed");
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);
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 (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;
}
if (cur->sk_flags & SK_ISNULL)
so->qual_ok = 0;
so->numberOfFirstKeys = 1;
return;
}
/* only one of >, >= */
if (init[BTGreaterStrategyNumber - 1]
&& init[BTGreaterEqualStrategyNumber - 1])
{
ScanKeyData *gt, *ge;
/* get space for the modified array of keys */
nbytes = BTMaxStrategyNumber * sizeof(ScanKeyData);
xform = (ScanKey) palloc(nbytes);
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++)
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;
}
so->numberOfKeys = new_numberOfKeys;
/* 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);
pfree(xform);
}
BTItem
_bt_formitem(IndexTuple itup)
{
int nbytes_btitem;
BTItem btitem;
Size tuplen;
extern Oid newoid();
int nbytes_btitem;
BTItem btitem;
Size tuplen;
extern Oid newoid();
/* see comments in btbuild
/*
* see comments in btbuild
*
* if (itup->t_info & INDEX_NULL_MASK) elog(WARN, "btree indices cannot
* include null keys");
*/
if (itup->t_info & INDEX_NULL_MASK)
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));
/* 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);
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;
BTScanOpaque so;
so = (BTScanOpaque) scan->opaque;
if (so->numberOfKeys > 0)
return (index_keytest(itup, RelationGetTupleDescriptor(scan->relation),
so->numberOfKeys, so->keyData));
else
return (true);
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;
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);
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;
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);
*keysok = 0;
if (keysz == 0)
return (true);
key = so->keyData;
tupdesc = RelationGetTupleDescriptor(scan->relation);
key = so->keyData;
tupdesc = RelationGetTupleDescriptor(scan->relation);
IncrIndexProcessed();
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 )
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,13 +1,13 @@
/*-------------------------------------------------------------------------
*
* 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 $
*
*-------------------------------------------------------------------------
*/
@ -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;
RetrieveIndexResult res;
/* if we have it cached in the scan desc, just return the value */
if ((res = rtscancache(s, dir)) != (RetrieveIndexResult) NULL)
/* 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;
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;
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);
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) {
while (n < FirstOffsetNumber || n > maxoff)
{
ReleaseBuffer(b);
if (so->s_stack == (RTSTACK *) NULL)
return ((RetrieveIndexResult) NULL);
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);
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);
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);
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;
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));
blk = ItemPointerGetBlockNumber(&(s->currentItemData));
n = ItemPointerGetOffsetNumber(&(s->currentItemData));
if (ScanDirectionIsForward(dir)) {
n = OffsetNumberNext(n);
} 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 (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;
OffsetNumber maxoff;
IndexTuple it;
RTreePageOpaque po;
RTreeScanOpaque so;
maxoff = PageGetMaxOffsetNumber(p);
po = (RTreePageOpaque) PageGetSpecialPointer(p);
so = (RTreeScanOpaque) s->opaque;
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 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;
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;
}
return (n);
if (ScanDirectionIsBackward(dir))
{
n = OffsetNumberPrev(n);
}
else
{
n = OffsetNumberNext(n);
}
}
return (n);
}
static RetrieveIndexResult
static RetrieveIndexResult
rtscancache(IndexScanDesc s, ScanDirection dir)
{
RetrieveIndexResult res;
ItemPointer ip;
RetrieveIndexResult res;
ItemPointer ip;
if (!(ScanDirectionIsNoMovement(dir)
&& ItemPointerIsValid(&(s->currentItemData)))) {
if (!(ScanDirectionIsNoMovement(dir)
&& ItemPointerIsValid(&(s->currentItemData))))
{
return ((RetrieveIndexResult) NULL);
}
return ((RetrieveIndexResult) NULL);
}
ip = rtheapptr(s->relation, &(s->currentItemData));
ip = rtheapptr(s->relation, &(s->currentItemData));
if (ItemPointerIsValid(ip))
res = FormRetrieveIndexResult(&(s->currentItemData), ip);
else
res = (RetrieveIndexResult) NULL;
if (ItemPointerIsValid(ip))
res = FormRetrieveIndexResult(&(s->currentItemData), ip);
else
res = (RetrieveIndexResult) NULL;
pfree (ip);
pfree(ip);
return (res);
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;
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);
}
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);
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;
BOX *n;
if ((n = (BOX *) palloc(sizeof (*n))) == (BOX *) NULL)
elog(WARN, "Cannot allocate box for union");
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);
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);
return (n);
}
BOX *
rt_box_inter(BOX *a, BOX *b)
BOX *
rt_box_inter(BOX * a, BOX * b)
{
BOX *n;
BOX *n;
if ((n = (BOX *) palloc(sizeof (*n))) == (BOX *) NULL)
elog(WARN, "Cannot allocate box for union");
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);
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);
}
if (n->high.x < n->low.x || n->high.y < n->low.y)
{
pfree(n);
return ((BOX *) NULL);
}
return (n);
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));
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;
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;
POLYGON *p;
p = (POLYGON *)PALLOCTYPE(POLYGON);
p = (POLYGON *) PALLOCTYPE(POLYGON);
if (!PointerIsValid(p))
elog(WARN, "Cannot allocate polygon for union");
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;
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;
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 *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)
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
{
pfree(p);
return ((POLYGON *) NULL);
xdim = (a->boundbox.high.x - a->boundbox.low.x);
ydim = (a->boundbox.high.y - a->boundbox.low.y);
*size = (float) (xdim * ydim);
}
return (p);
return;
}
POLYGON *
rt_poly_inter(POLYGON * a, POLYGON * b)
{
POLYGON *p;
p = (POLYGON *) PALLOCTYPE(POLYGON);
if (!PointerIsValid(p))
elog(WARN, "Cannot allocate polygon for intersection");
memset((char *) p, 0, sizeof(POLYGON)); /* zero any holes */
p->size = sizeof(POLYGON);
p->npts = 0;
p->boundbox.high.x = Min(a->boundbox.high.x, b->boundbox.high.x);
p->boundbox.high.y = Min(a->boundbox.high.y, b->boundbox.high.y);
p->boundbox.low.x = Max(a->boundbox.low.x, b->boundbox.low.x);
p->boundbox.low.y = Max(a->boundbox.low.y, b->boundbox.low.y);
if (p->boundbox.high.x < p->boundbox.low.x || p->boundbox.high.y < p->boundbox.low.y)
{
pfree(p);
return ((POLYGON *) NULL);
}
return (p);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,13 @@
/*-------------------------------------------------------------------------
*
* rtscan.c--
* 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 $
*
*-------------------------------------------------------------------------
*/
@ -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;
IndexScanDesc s;
RelationSetLockForRead(r);
s = RelationGetIndexScan(r, fromEnd, nkeys, key);
rtregscan(s);
RelationSetLockForRead(r);
s = RelationGetIndexScan(r, fromEnd, nkeys, key);
rtregscan(s);
return (s);
return (s);
}
void
rtrescan(IndexScanDesc s, bool fromEnd, ScanKey key)
{
RTreeScanOpaque p;
RegProcedure internal_proc;
int i;
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++)
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).
*/
/*
* Clear all the pointers.
*/
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);
}
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;
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;
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;
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;
}
/* 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;
freestack(p->s_markstk);
p->s_markstk = o;
}
void
rtrestrpos(IndexScanDesc s)
{
RTreeScanOpaque p;
RTSTACK *o, *n, *tmp;
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;
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;
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;
}
/* 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;
freestack(p->s_stack);
p->s_stack = o;
}
void
rtendscan(IndexScanDesc s)
{
RTreeScanOpaque p;
RTreeScanOpaque p;
p = (RTreeScanOpaque) s->opaque;
p = (RTreeScanOpaque) s->opaque;
if (p != (RTreeScanOpaque) NULL) {
freestack(p->s_stack);
freestack(p->s_markstk);
pfree (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 */
rtdropscan(s);
/* XXX don't unset read lock -- two-phase locking */
}
static void
rtregscan(IndexScanDesc s)
{
RTScanList l;
RTScanList l;
l = (RTScanList) palloc(sizeof(RTScanListData));
l->rtsl_scan = s;
l->rtsl_next = RTScans;
RTScans = 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;
RTScanList l;
RTScanList prev;
prev = (RTScanList) NULL;
prev = (RTScanList) NULL;
for (l = RTScans;
l != (RTScanList) NULL && l->rtsl_scan != s;
l = l->rtsl_next) {
prev = l;
}
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 (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;
if (prev == (RTScanList) NULL)
RTScans = l->rtsl_next;
else
prev->rtsl_next = l->rtsl_next;
pfree(l);
pfree(l);
}
void
rtadjscans(Relation r, int op, BlockNumber blkno, OffsetNumber offnum)
{
RTScanList l;
Oid relid;
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);
}
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;
RTreeScanOpaque so;
adjustiptr(s, &(s->currentItemData), op, blkno, offnum);
adjustiptr(s, &(s->currentMarkData), op, blkno, offnum);
adjustiptr(s, &(s->currentItemData), op, blkno, offnum);
adjustiptr(s, &(s->currentMarkData), op, blkno, offnum);
so = (RTreeScanOpaque) s->opaque;
so = (RTreeScanOpaque) s->opaque;
if (op == RTOP_SPLIT) {
adjuststack(so->s_stack, blkno, offnum);
adjuststack(so->s_markstk, blkno, offnum);
}
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;
OffsetNumber curoff;
RTreeScanOpaque so;
if (ItemPointerIsValid(iptr)) {
if (ItemPointerGetBlockNumber(iptr) == blkno) {
curoff = ItemPointerGetOffsetNumber(iptr);
so = (RTreeScanOpaque) s->opaque;
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) {
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;
}
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;
while (stk != (RTSTACK *) NULL)
{
if (stk->rts_blk == blkno)
stk->rts_child = FirstOffsetNumber;
stk = stk->rts_parent;
}
stk = stk->rts_parent;
}
}

View File

@ -1,13 +1,13 @@
/*-------------------------------------------------------------------------
*
* rtstrat.c--
* 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 $
*
*-------------------------------------------------------------------------
*/
@ -18,224 +18,226 @@
#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;
StrategyNumber procstrat;
StrategyMap strategyMap;
procstrat = RelationGetRTStrategy(r, attnum, proc);
strategyMap = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(r),
RTNStrategies,
attnum);
procstrat = RelationGetRTStrategy(r, attnum, proc);
strategyMap = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(r),
RTNStrategies,
attnum);
return (strategyMap->entry[RTOperMap[procstrat - 1] - 1].sk_procedure);
return (strategyMap->entry[RTOperMap[procstrat - 1] - 1].sk_procedure);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +1,18 @@
/*-------------------------------------------------------------------------
*
* xid.c--
* 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;
/* return(TransactionIdFormString(transactionId)); */
char *representation;
/* maximum 32 bit unsigned integer representation takes 10 chars */
representation = palloc(11);
/* maximum 32 bit unsigned integer representation takes 10 chars */
representation = palloc(11);
sprintf(representation, "%u", transactionId);
sprintf(representation, "%u", transactionId);
return (representation);
return (representation);
}
/* ----------------------------------------------------------------
* StoreInvalidTransactionId
* StoreInvalidTransactionId
*
* Maybe do away with Pointer types in these routines.
* 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

@ -7,7 +7,7 @@
*
*
* 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;
char *path;
if (IsSharedSystemRelationName(relname)) {
path = (char *) palloc(strlen(DataDir) + sizeof(NameData) + 2);
sprintf(path, "%s/%s", DataDir, relname);
return (path);
}
return(relname);
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;
int i;
/*
* Quick out: if it's not a system relation, it can't be a shared
* system relation.
*/
if (!IsSystemRelationName(relname))
/*
* 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;
Oid lastoid;
GetNewObjectId(&lastoid);
if (! OidIsValid(lastoid))
elog(WARN, "newoid: GetNewObjectId returns invalid oid");
return 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;
int i;
for (i=0; i<nIndices; i++)
for (i = 0; i < nIndices; i++)
{
idescs[i] = index_openr(names[i]);
idescs[i] = index_openr(names[i]);
}
}
@ -87,12 +88,12 @@ CatalogOpenIndices(int nIndices, char *names[], Relation idescs[])
* This is the inverse routine to CatalogOpenIndices()
*/
void
CatalogCloseIndices(int nIndices, Relation *idescs)
CatalogCloseIndices(int nIndices, Relation * idescs)
{
int i;
int i;
for (i=0; i<nIndices; i++)
index_close(idescs[i]);
for (i = 0; i < nIndices; i++)
index_close(idescs[i]);
}
@ -102,68 +103,69 @@ CatalogCloseIndices(int nIndices, Relation *idescs)
* 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;
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);
heapDescriptor = RelationGetTupleDescriptor(heapRelation);
for (i=0; i<nIndices; i++)
for (i = 0; i < nIndices; i++)
{
TupleDesc indexDescriptor;
InsertIndexResult indexRes;
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);
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++)
;
/*
* 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)
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;
else
finfoP = (FuncIndexInfo *) NULL;
FormIndexDatum(natts,
(AttrNumber *)&pgIndexP->indkey[0],
heapTuple,
heapDescriptor,
InvalidBuffer,
&datum,
nulls,
finfoP);
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);
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;
Relation pg_class;
HeapTuple htup;
Form_pg_class pgRelP;
int i;
Assert(IsSystemRelationName(catName));
Assert(IsSystemRelationName(catName));
/*
* If we're bootstraping we don't have pg_class (or any indices).
*/
if (IsBootstrapProcessingMode())
return false;
/*
* 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);
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);
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;
}
if (!HeapTupleIsValid(htup))
{
elog(NOTICE, "CatalogHasIndex: no relation with oid %d", catId);
return false;
}
pgRelP = (Form_pg_class)GETSTRUCT(htup);
return (pgRelP->relhasindex);
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;
IndexScanDesc sd;
RetrieveIndexResult indexRes;
HeapTuple tuple;
Buffer buffer;
sd = index_beginscan(idesc, false, 1, skey);
tuple = (HeapTuple)NULL;
sd = index_beginscan(idesc, false, 1, skey);
tuple = (HeapTuple) NULL;
do {
indexRes = index_getnext(sd, ForwardScanDirection);
if (indexRes) {
ItemPointer iptr;
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));
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);
}
if (HeapTupleIsValid(tuple))
{
tuple = heap_copytuple(tuple);
ReleaseBuffer(buffer);
}
index_endscan(sd);
pfree(sd);
return (tuple);
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;
Relation idesc;
ScanKeyData skey;
OidName keyarg;
HeapTuple tuple;
keyarg = mkoidname(relid, attname);
ScanKeyEntryInitialize(&skey,
(bits16)0x0,
(AttrNumber)1,
(RegProcedure)OidNameEqRegProcedure,
(Datum)keyarg);
keyarg = mkoidname(relid, attname);
ScanKeyEntryInitialize(&skey,
(bits16) 0x0,
(AttrNumber) 1,
(RegProcedure) OidNameEqRegProcedure,
(Datum) keyarg);
idesc = index_openr(AttributeNameIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
idesc = index_openr(AttributeNameIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
index_close(idesc);
pfree(keyarg);
index_close(idesc);
pfree(keyarg);
return tuple;
return tuple;
}
HeapTuple
AttributeNumIndexScan(Relation heapRelation,
Oid relid,
AttrNumber attnum)
Oid relid,
AttrNumber attnum)
{
Relation idesc;
ScanKeyData skey;
OidInt2 keyarg;
HeapTuple tuple;
Relation idesc;
ScanKeyData skey;
OidInt2 keyarg;
HeapTuple tuple;
keyarg = mkoidint2(relid, (uint16)attnum);
ScanKeyEntryInitialize(&skey,
(bits16)0x0,
(AttrNumber)1,
(RegProcedure)OidInt2EqRegProcedure,
(Datum)keyarg);
keyarg = mkoidint2(relid, (uint16) attnum);
ScanKeyEntryInitialize(&skey,
(bits16) 0x0,
(AttrNumber) 1,
(RegProcedure) OidInt2EqRegProcedure,
(Datum) keyarg);
idesc = index_openr(AttributeNumIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
idesc = index_openr(AttributeNumIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
index_close(idesc);
pfree(keyarg);
index_close(idesc);
pfree(keyarg);
return tuple;
return tuple;
}
HeapTuple
ProcedureOidIndexScan(Relation heapRelation, Oid procId)
{
Relation idesc;
ScanKeyData skey;
HeapTuple tuple;
Relation idesc;
ScanKeyData skey;
HeapTuple tuple;
ScanKeyEntryInitialize(&skey,
(bits16)0x0,
(AttrNumber)1,
(RegProcedure)ObjectIdEqualRegProcedure,
(Datum)procId);
ScanKeyEntryInitialize(&skey,
(bits16) 0x0,
(AttrNumber) 1,
(RegProcedure) ObjectIdEqualRegProcedure,
(Datum) procId);
idesc = index_openr(ProcedureOidIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
idesc = index_openr(ProcedureOidIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
index_close(idesc);
index_close(idesc);
return tuple;
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.
*/
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;
ScanKeyEntryInitialize(&skey,
(bits16)0x0,
(AttrNumber)1,
(RegProcedure)NameEqualRegProcedure,
(Datum)procName);
/*
* The index scan is complete, i.e. we've scanned everything there is
* to scan.
*/
bool FoundMatch;
idesc = index_openr(ProcedureNameIndex);
/*
* In scanning pg_proc, we have found a row that meets our search
* criteria.
*/
sd = index_beginscan(idesc, false, 1, &skey);
ScanKeyEntryInitialize(&skey,
(bits16) 0x0,
(AttrNumber) 1,
(RegProcedure) NameEqualRegProcedure,
(Datum) procName);
/*
* 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;
idesc = index_openr(ProcedureNameIndex);
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;
}
sd = index_beginscan(idesc, false, 1, &skey);
if (FoundMatch) {
Assert(HeapTupleIsValid(tuple));
return_tuple = heap_copytuple(tuple);
ReleaseBuffer(buffer);
} else return_tuple = (HeapTuple)NULL;
/*
* 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;
index_endscan(sd);
index_close(idesc);
iptr = &indexRes->heap_iptr;
tuple = heap_fetch(heapRelation, NowTimeQual, iptr, &buffer);
pfree(indexRes);
if (HeapTupleIsValid(tuple))
{
return return_tuple;
/*
* Here's a row for a procedure that has the sought
* procedure name. To be a match, though, we need it to
* have the right number and type of arguments too, so we
* check that now.
*/
pgProcP = (Form_pg_proc) GETSTRUCT(tuple);
if (pgProcP->pronargs == nargs &&
oid8eq(&(pgProcP->proargtypes[0]), argTypes))
FoundMatch = true;
else
ReleaseBuffer(buffer);
}
}
else
ScanComplete = true;
}
if (FoundMatch)
{
Assert(HeapTupleIsValid(tuple));
return_tuple = heap_copytuple(tuple);
ReleaseBuffer(buffer);
}
else
return_tuple = (HeapTuple) NULL;
index_endscan(sd);
index_close(idesc);
return return_tuple;
}
HeapTuple
ProcedureSrcIndexScan(Relation heapRelation, text *procSrc)
ProcedureSrcIndexScan(Relation heapRelation, text * procSrc)
{
Relation idesc;
IndexScanDesc sd;
ScanKeyData skey;
RetrieveIndexResult indexRes;
HeapTuple tuple;
Buffer buffer;
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);
ScanKeyEntryInitialize(&skey,
(bits16) 0x0,
(AttrNumber) Anum_pg_proc_prosrc,
(RegProcedure) TextEqualRegProcedure,
(Datum) procSrc);
idesc = index_openr(ProcedureSrcIndex);
sd = index_beginscan(idesc, false, 1, &skey);
idesc = index_openr(ProcedureSrcIndex);
sd = index_beginscan(idesc, false, 1, &skey);
indexRes = index_getnext(sd, ForwardScanDirection);
if (indexRes) {
ItemPointer iptr;
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;
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);
}
if (HeapTupleIsValid(tuple))
{
tuple = heap_copytuple(tuple);
ReleaseBuffer(buffer);
}
index_endscan(sd);
index_endscan(sd);
return tuple;
return tuple;
}
HeapTuple
TypeOidIndexScan(Relation heapRelation, Oid typeId)
{
Relation idesc;
ScanKeyData skey;
HeapTuple tuple;
Relation idesc;
ScanKeyData skey;
HeapTuple tuple;
ScanKeyEntryInitialize(&skey,
(bits16)0x0,
(AttrNumber)1,
(RegProcedure)ObjectIdEqualRegProcedure,
(Datum)typeId);
ScanKeyEntryInitialize(&skey,
(bits16) 0x0,
(AttrNumber) 1,
(RegProcedure) ObjectIdEqualRegProcedure,
(Datum) typeId);
idesc = index_openr(TypeOidIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
idesc = index_openr(TypeOidIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
index_close(idesc);
index_close(idesc);
return tuple;
return tuple;
}
HeapTuple
TypeNameIndexScan(Relation heapRelation, char *typeName)
{
Relation idesc;
ScanKeyData skey;
HeapTuple tuple;
Relation idesc;
ScanKeyData skey;
HeapTuple tuple;
ScanKeyEntryInitialize(&skey,
(bits16)0x0,
(AttrNumber)1,
(RegProcedure)NameEqualRegProcedure,
(Datum)typeName);
ScanKeyEntryInitialize(&skey,
(bits16) 0x0,
(AttrNumber) 1,
(RegProcedure) NameEqualRegProcedure,
(Datum) typeName);
idesc = index_openr(TypeNameIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
idesc = index_openr(TypeNameIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
index_close(idesc);
index_close(idesc);
return tuple;
return tuple;
}
HeapTuple
ClassNameIndexScan(Relation heapRelation, char *relName)
{
Relation idesc;
ScanKeyData skey;
HeapTuple tuple;
Relation idesc;
ScanKeyData skey;
HeapTuple tuple;
ScanKeyEntryInitialize(&skey,
(bits16)0x0,
(AttrNumber)1,
(RegProcedure)NameEqualRegProcedure,
(Datum)relName);
ScanKeyEntryInitialize(&skey,
(bits16) 0x0,
(AttrNumber) 1,
(RegProcedure) NameEqualRegProcedure,
(Datum) relName);
idesc = index_openr(ClassNameIndex);
idesc = index_openr(ClassNameIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
index_close(idesc);
return tuple;
index_close(idesc);
return tuple;
}
HeapTuple
ClassOidIndexScan(Relation heapRelation, Oid relId)
{
Relation idesc;
ScanKeyData skey;
HeapTuple tuple;
Relation idesc;
ScanKeyData skey;
HeapTuple tuple;
ScanKeyEntryInitialize(&skey,
(bits16)0x0,
(AttrNumber)1,
(RegProcedure)ObjectIdEqualRegProcedure,
(Datum)relId);
ScanKeyEntryInitialize(&skey,
(bits16) 0x0,
(AttrNumber) 1,
(RegProcedure) ObjectIdEqualRegProcedure,
(Datum) relId);
idesc = index_openr(ClassOidIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
idesc = index_openr(ClassOidIndex);
tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
index_close(idesc);
index_close(idesc);
return tuple;
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
/* ----------------
@ -38,284 +38,299 @@
* 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;
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));
memset(fnArgs, 0, 8 * sizeof(Oid));
/* sanity checks */
if (!aggName)
elog(WARN, "AggregateCreate: no aggregate name supplied");
/* 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");
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;
PointerGetDatum(aggbasetypeName),
0, 0, 0);
if (!HeapTupleIsValid(tup))
elog(WARN, "AggregateCreate: Type '%s' undefined", aggbasetypeName);
xbase = 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 (aggtransfn1Name)
{
tup = SearchSysCacheTuple(TYPNAME,
PointerGetDatum(aggtransfn1typeName),
0, 0, 0);
if (!HeapTupleIsValid(tup))
elog(WARN, "AggregateCreate: Type '%s' undefined",
aggtransfn1typeName);
xret1 = tup->t_oid;
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] = 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);
}
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);
}
if (aggtransfn2Name)
{
tup = SearchSysCacheTuple(TYPNAME,
PointerGetDatum(aggtransfn2typeName),
0, 0, 0);
if (!HeapTupleIsValid(tup))
elog(WARN, "AggregateCreate: Type '%s' undefined",
aggtransfn2typeName);
xret2 = tup->t_oid;
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);
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);
}
/* more sanity checks */
if (aggtransfn1Name && aggtransfn2Name && !aggfinalfnName)
elog(WARN, "AggregateCreate: Aggregate must have final function with both transition functions");
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);
if ((!aggtransfn1Name || !aggtransfn2Name) && aggfinalfnName)
elog(WARN, "AggregateCreate: Aggregate cannot have final function without both transition functions");
/* more sanity checks */
if (aggtransfn1Name && aggtransfn2Name && !aggfinalfnName)
elog(WARN, "AggregateCreate: Aggregate must have final function with 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 ((!aggtransfn1Name || !aggtransfn2Name) && aggfinalfnName)
elog(WARN, "AggregateCreate: Aggregate cannot have final function without both transition functions");
/*
* 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");
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);
}
/* 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);
/*
* 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");
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);
}
/* 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);
if (agginitval1)
values[Anum_pg_aggregate_agginitval1-1] = PointerGetDatum(textin(agginitval1));
else
nulls[Anum_pg_aggregate_agginitval1-1] = 'n';
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 (agginitval2)
values[Anum_pg_aggregate_agginitval2-1] = PointerGetDatum(textin(agginitval2));
else
nulls[Anum_pg_aggregate_agginitval2-1] = 'n';
if (agginitval1)
values[Anum_pg_aggregate_agginitval1 - 1] = PointerGetDatum(textin(agginitval1));
else
nulls[Anum_pg_aggregate_agginitval1 - 1] = 'n';
if (!RelationIsValid(aggdesc = heap_openr(AggregateRelationName)))
elog(WARN, "AggregateCreate: could not open '%s'",
AggregateRelationName);
if (agginitval2)
values[Anum_pg_aggregate_agginitval2 - 1] = PointerGetDatum(textin(agginitval2));
else
nulls[Anum_pg_aggregate_agginitval2 - 1] = 'n';
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 (!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;
HeapTuple tup;
Relation aggRel;
int initValAttno;
Oid transtype;
text *textInitVal;
char *strInitVal,
*initVal;
Assert(PointerIsValid(aggName));
Assert(PointerIsValid(isNull));
Assert(xfuncno == 1 || xfuncno == 2);
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;
}
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) {
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;
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.
/* ----------------
* sanity checks
* ----------------
*/
if (!strcmp(procedureName, GENERICSETNAME)) {
prosrctext = textin(prosrc);
tup = SearchSysCacheTuple(PROSRC,
PointerGetDatum(prosrctext),
0,0,0);
if (HeapTupleIsValid(tup))
return tup->t_oid;
}
}
Assert(PointerIsValid(prosrc));
Assert(PointerIsValid(probin));
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)
parameterCount = 0;
memset(typev, 0, 8 * sizeof(Oid));
foreach(x, argList)
{
Relation idescs[Num_pg_proc_indices];
Value *t = lfirst(x);
CatalogOpenIndices(Num_pg_proc_indices, Name_pg_proc_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_proc_indices, rdesc, tup);
CatalogCloseIndices(Num_pg_proc_indices, idescs);
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;
}
heap_close(rdesc);
return tup->t_oid;
}
tup = SearchSysCacheTuple(PRONAME,
PointerGetDatum(procedureName),
UInt16GetDatum(parameterCount),
PointerGetDatum(typev),
0);
if (HeapTupleIsValid(tup))
elog(WARN, "ProcedureCreate: procedure %s already exists with same arguments",
procedureName);
if (!strcmp(languageName, "sql"))
{
/*
* If this call is defining a set, check if the set is already
* defined by looking to see whether this call's function text
* matches a function already in pg_proc. If so just return the
* OID of the existing set.
*/
if (!strcmp(procedureName, GENERICSETNAME))
{
prosrctext = textin(prosrc);
tup = SearchSysCacheTuple(PROSRC,
PointerGetDatum(prosrctext),
0, 0, 0);
if (HeapTupleIsValid(tup))
return tup->t_oid;
}
}
tup = SearchSysCacheTuple(LANNAME,
PointerGetDatum(languageName),
0, 0, 0);
if (!HeapTupleIsValid(tup))
elog(WARN, "ProcedureCreate: no such language %s",
languageName);
languageObjectId = tup->t_oid;
if (strcmp(returnTypeName, "opaque") == 0)
{
if (strcmp(languageName, "sql") == 0)
{
elog(WARN, "ProcedureCreate: sql functions cannot return type \"opaque\"");
}
typeObjectId = 0;
}
else
{
typeObjectId = TypeGet(returnTypeName, &defined);
if (!OidIsValid(typeObjectId))
{
elog(NOTICE, "ProcedureCreate: type '%s' is not yet defined",
returnTypeName);
#if 0
elog(NOTICE, "ProcedureCreate: creating a shell for type '%s'",
returnTypeName);
#endif
typeObjectId = TypeShellMake(returnTypeName);
if (!OidIsValid(typeObjectId))
{
elog(WARN, "ProcedureCreate: could not create type '%s'",
returnTypeName);
}
}
else if (!defined)
{
elog(NOTICE, "ProcedureCreate: return type '%s' is only a shell",
returnTypeName);
}
}
/*
* don't allow functions of complex types that have the same name as
* existing attributes of the type
*/
if (parameterCount == 1 &&
(toid = TypeGet(strVal(lfirst(argList)), &defined)) &&
defined &&
(relid = typeid_get_relid(toid)) != 0 &&
get_attnum(relid, procedureName) != InvalidAttrNumber)
elog(WARN, "method %s already an attribute of type %s",
procedureName, strVal(lfirst(argList)));
/*
* If this is a postquel procedure, we parse it here in order to be
* sure that it contains no syntax errors. We should store the plan
* in an Inversion file for use later, but for now, we just store the
* procedure's text in the prosrc attribute.
*/
if (strcmp(languageName, "sql") == 0)
{
plan_list = pg_plan(prosrc, typev, parameterCount,
&querytree_list, dest);
/* typecheck return value */
pg_checkretval(typeObjectId, querytree_list);
}
for (i = 0; i < Natts_pg_proc; ++i)
{
nulls[i] = ' ';
values[i] = (Datum) NULL;
}
i = 0;
values[i++] = PointerGetDatum(procedureName);
values[i++] = Int32GetDatum(GetUserId());
values[i++] = ObjectIdGetDatum(languageObjectId);
/* XXX isinherited is always false for now */
values[i++] = Int8GetDatum((bool) 0);
/* XXX istrusted is always false for now */
values[i++] = Int8GetDatum(trusted);
values[i++] = Int8GetDatum(canCache);
values[i++] = UInt16GetDatum(parameterCount);
values[i++] = Int8GetDatum(returnsSet);
values[i++] = ObjectIdGetDatum(typeObjectId);
values[i++] = (Datum) typev;
/*
* The following assignments of constants are made. The real values
* will have to be extracted from the arglist someday soon.
*/
values[i++] = Int32GetDatum(byte_pct); /* probyte_pct */
values[i++] = Int32GetDatum(perbyte_cpu); /* properbyte_cpu */
values[i++] = Int32GetDatum(percall_cpu); /* propercall_cpu */
values[i++] = Int32GetDatum(outin_ratio); /* prooutin_ratio */
values[i++] = (Datum) fmgr(TextInRegProcedure, prosrc); /* prosrc */
values[i++] = (Datum) fmgr(TextInRegProcedure, probin); /* probin */
rdesc = heap_openr(ProcedureRelationName);
tupDesc = rdesc->rd_att;
tup = heap_formtuple(tupDesc,
values,
nulls);
heap_insert(rdesc, tup);
if (RelationGetRelationTupleForm(rdesc)->relhasindex)
{
Relation idescs[Num_pg_proc_indices];
CatalogOpenIndices(Num_pg_proc_indices, Name_pg_proc_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_proc_indices, rdesc, tup);
CatalogCloseIndices(Num_pg_proc_indices, idescs);
}
heap_close(rdesc);
return tup->t_oid;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,23 +1,23 @@
/*-------------------------------------------------------------------------
*
* version.c--
* 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,19 +34,21 @@
#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.
*
@ -55,8 +57,8 @@ static char attr_list[MAX_QUERY_LEN];
*/
/*
* 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,90 +82,96 @@ 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);
}
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);
/*
* 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);
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];
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);
/*
* 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);
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_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);
/*
* Creating the ``v_deleted'' relation.
*/
sprintf(query_buf, "CREATE TABLE %s_del (DOID oid)", vname);
eval_as_new_xact(query_buf);
}
#endif
@ -176,38 +184,44 @@ VersionCreate(char *vname, char *bname)
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);
}
maxattrs = RelationGetNumberOfAttributes(rdesc);
heap_close(rdesc);
attr_list[0] = '\0';
return;
for (i = maxattrs - 1; i > -1; --i)
{
attrname = (rdesc->rd_att->attrs[i]->attname).data;
if (notfirst == 1)
{
sprintf(temp_buf, ", %s = new.%s", attrname, attrname);
}
else
{
sprintf(temp_buf, "%s = new.%s", attrname, attrname);
notfirst = 1;
}
strcat(attr_list, temp_buf);
}
heap_close(rdesc);
return;
}
#endif
/*
@ -219,128 +233,131 @@ setAttrList(char *bname)
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);
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);
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);
vname, vname, vname, vname, bname,
bname, snapshot,
vname, vname, bname, bname, vname);
eval_as_new_xact(rule_buf);
eval_as_new_xact(rule_buf);
/* printf("%s\n",rule_buf); */
/* printf("%s\n",rule_buf); */
}
#endif
/*
* 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); */
}

View File

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

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.
* Create new (temporary) relations for the base heap and the new
* index.
*
* Exclusively lock the relations.
* Exclusively lock the relations.
*
* Create new clustered index and base heap relation.
* Create new clustered index and base heap relation.
*
*/
void
cluster(char oldrelname[], char oldindexname[])
{
Oid OIDOldHeap, OIDOldIndex, OIDNewHeap;
Oid OIDOldHeap,
OIDOldIndex,
OIDNewHeap;
Relation OldHeap, OldIndex;
Relation NewHeap;
Relation OldHeap,
OldIndex;
Relation NewHeap;
char NewIndexName[NAMEDATALEN];
char NewHeapName[NAMEDATALEN];
char saveoldrelname[NAMEDATALEN];
char saveoldindexname[NAMEDATALEN];
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);
/*
* 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.
*/
/*
* 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.
*
*/
/* matches the StartTransaction in PostgresMain() */
/*
* Like vacuum, cluster spans transactions, so I'm going to handle it
* in the same way.
*/
OldHeap = heap_openr(oldrelname);
if (!RelationIsValid(OldHeap)) {
elog(WARN, "cluster: unknown relation: \"%s\"",
oldrelname);
}
OIDOldHeap = OldHeap->rd_id; /* Get OID for the index scan */
/* matches the StartTransaction in PostgresMain() */
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 */
OldHeap = heap_openr(oldrelname);
if (!RelationIsValid(OldHeap))
{
elog(WARN, "cluster: unknown relation: \"%s\"",
oldrelname);
}
OIDOldHeap = OldHeap->rd_id;/* Get OID for the index scan */
heap_close(OldHeap);
index_close(OldIndex);
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 */
/*
* 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);
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();
/* To make the new heap visible (which is until now empty). */
CommandCounterIncrement();
rebuildheap(OIDNewHeap, OIDOldHeap, OIDOldIndex);
rebuildheap(OIDNewHeap, OIDOldHeap, OIDOldIndex);
/* To flush the filled new heap (and the statistics about it). */
CommandCounterIncrement();
/* 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);
/* 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();
/*
* 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);
/* Destroy old heap (along with its index) and rename new. */
heap_destroy(oldrelname);
renamerel(NewHeapName, saveoldrelname);
TypeRename(NewHeapName, saveoldrelname);
renamerel(NewHeapName, saveoldrelname);
TypeRename(NewHeapName, saveoldrelname);
renamerel(NewIndexName, saveoldindexname);
renamerel(NewIndexName, saveoldindexname);
/*
* Again flush all the buffers.
*/
CommitTransactionCommand();
StartTransactionCommand();
/*
* 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);
tupdesc = CreateTupleDescCopy(OldHeapDesc);
OIDNewHeap=heap_create(NewName,
NULL,
OldHeap->rd_rel->relarch,
OldHeap->rd_rel->relsmgr,
tupdesc);
OIDNewHeap = heap_create(NewName,
NULL,
OldHeap->rd_rel->relarch,
OldHeap->rd_rel->relsmgr,
tupdesc);
if (!OidIsValid(OIDNewHeap))
elog(WARN,"clusterheap: cannot create temporary heap relation\n");
if (!OidIsValid(OIDNewHeap))
elog(WARN, "clusterheap: cannot create temporary heap relation\n");
NewHeap=heap_open(OIDNewHeap);
NewHeap = heap_open(OIDNewHeap);
heap_close(NewHeap);
heap_close(OldHeap);
heap_close(NewHeap);
heap_close(OldHeap);
return NewHeap;
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,22 +1,22 @@
/*-------------------------------------------------------------------------
*
* 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 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 PerformAddAttribute() code, like most of the relation
* manipulating code in the commands/ directory, should go
* someplace closer to the lib/catalog code.
*
*-------------------------------------------------------------------------
*/
@ -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;
MemoryContext context;
/* ----------------
* sanity checks
* ----------------
*/
AssertArg(PortalIsValid(portal));
AssertArg(portal->cleanup == PortalCleanup);
/* ----------------
* 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);
/* ----------------
* 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));
/* ----------------
* tell the executor to shutdown the query
* ----------------
*/
ExecutorEnd(PortalGetQueryDesc(portal), PortalGetState(portal));
/* ----------------
* switch back to previous context
* ----------------
*/
MemoryContextSwitchTo(context);
PortalExecutorHeapMemory = (MemoryContext) NULL;
/* ----------------
* 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;
Portal portal;
int feature;
QueryDesc *queryDesc;
MemoryContext context;
/* ----------------
* sanity checks
* ----------------
*/
if (name == NULL) {
elog(NOTICE, "PerformPortalFetch: blank portal unsupported");
return;
}
/* ----------------
* 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;
}
/* ----------------
* 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));
/* ----------------
* switch into the portal context
* ----------------
*/
context = MemoryContextSwitchTo((MemoryContext) PortalGetHeapMemory(portal));
AssertState(context ==
(MemoryContext)PortalGetHeapMemory(GetPortalByName(NULL)));
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;
/* ----------------
* 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);
/* ----------------
* 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);
/* ----------------
* execute the portal fetch operation
* ----------------
*/
PortalExecutorHeapMemory = (MemoryContext)
PortalGetHeapMemory(portal);
ExecutorRun(queryDesc, PortalGetState(portal), feature, count);
ExecutorRun(queryDesc, PortalGetState(portal), feature, count);
/* ----------------
* Note: the "end-of-command" tag is returned by higher-level
* 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)));
/* ----------------
* 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;
Portal portal;
/* ----------------
* sanity checks
* ----------------
*/
if (name == NULL) {
elog(NOTICE, "PerformPortalClose: blank portal unsupported");
return;
}
/* ----------------
* 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;
}
/* ----------------
* 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);
/* ----------------
* 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)
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;
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);
/*
* 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);
/*
* 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");
/* this routine is actually in the planner */
children = find_all_inheritors(lconsi(myrelid,NIL), NIL);
/*
* if the first element in the 'schema' list is a "*" then we are
* supposed to add this attribute to all classes that inherit from
* 'relationName' (as well as to 'relationName').
*
* any permissions or problems with duplicate attributes will cause the
* whole transaction to abort, which is what we want -- all or
* nothing.
*/
if (colDef != NULL)
{
if (inherits)
{
Oid myrelid,
childrelid;
List *child,
*children;
/*
* 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);
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);
ScanKeyEntryInitialize(&key[0],
(bits16) NULL,
(AttrNumber) Anum_pg_attribute_attrelid,
(RegProcedure) ObjectIdEqualRegProcedure,
(Datum) reltup->t_oid);
((Form_pg_class) GETSTRUCT(reltup))->relnatts = maxatts;
oldTID = reltup->t_ctid;
heap_replace(relrdesc, &oldTID, reltup);
ScanKeyEntryInitialize(&key[1],
(bits16) NULL,
(AttrNumber) Anum_pg_attribute_attname,
(RegProcedure) NameEqualRegProcedure,
(Datum) NULL);
/* 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);
attributeD.attrelid = reltup->t_oid;
attributeD.attdisbursion = 0; /* XXX temporary */
attributeD.attcacheoff = -1;
pfree(reltup);
heap_close(relrdesc);
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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,13 @@
/*-------------------------------------------------------------------------
*
* explain.c--
* Explain 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);
if (IsAbortedTransactionBlockState())
{
char *tag = "*ABORT STATE*";
elog(NOTICE, "(transaction aborted): %s",
"queries ignored until END");
EndCommand(tag, dest);
return;
}
elog(NOTICE, "(transaction aborted): %s",
"queries ignored until END");
/* 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);
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;
char *pname;
char buf[1000];
int i;
if (plan==NULL) {
appendStringInfo(str, "\n");
return;
}
switch(nodeTag(plan)) {
case T_Result:
pname = "Result";
break;
case T_Append:
pname = "Append";
break;
case T_NestLoop:
pname = "Nested Loop";
break;
case T_MergeJoin:
pname = "Merge Join";
break;
case T_HashJoin:
pname = "Hash Join";
break;
case T_SeqScan:
pname = "Seq Scan";
break;
case T_IndexScan:
pname = "Index Scan";
break;
case T_Temp:
pname = "Temp Scan";
break;
case T_Sort:
pname = "Sort";
break;
case T_Group:
pname = "Group";
break;
case T_Agg:
pname = "Aggregate";
break;
case T_Unique:
pname = "Unique";
break;
case T_Hash:
pname = "Hash";
break;
case T_Tee:
pname = "Tee";
break;
default:
pname = "";
break;
}
for(i=0; i < indent; i++)
appendStringInfo(str, " ");
appendStringInfo(str, pname);
switch(nodeTag(plan)) {
case T_SeqScan:
case T_IndexScan:
if (((Scan*)plan)->scanrelid > 0) {
RangeTblEntry *rte = nth(((Scan*)plan)->scanrelid-1, es->rtable);
sprintf(buf, " on %s", rte->refname);
appendStringInfo(str, buf);
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;
StringInfo str;
char *s;
if (plan==NULL)
return "";
Assert(plan!=NULL);
str = makeStringInfo();
explain_outNode(str, plan, 0, es);
s = str->data;
pfree(str);
if (plan == NULL)
return "";
Assert(plan != NULL);
str = makeStringInfo();
explain_outNode(str, plan, 0, es);
s = str->data;
pfree(str);
return s;
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];
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");
/*
* 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");
}
}
}
#ifdef PURGEDEBUG
elog(DEBUG, "%s: absolute time `%s' is %d.",
cmdname, absoluteTimeString, absoluteTime);
#endif /* defined(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);
}
relativeTime = reltimein(relativeTimeString);
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) */
}
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);
/*
* 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);
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);
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;
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 (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[0], 0x0,
Anum_pg_operator_oprname,
NameEqualRegProcedure,
PointerGetDatum(operatorName));
ScanKeyEntryInitialize(&operatorKey[1], 0x0,
Anum_pg_operator_oprleft,
ObjectIdEqualRegProcedure,
ObjectIdGetDatum(typeId1));
ScanKeyEntryInitialize(&operatorKey[1], 0x0,
Anum_pg_operator_oprleft,
ObjectIdEqualRegProcedure,
ObjectIdGetDatum(typeId1));
ScanKeyEntryInitialize(&operatorKey[2], 0x0,
Anum_pg_operator_oprright,
ObjectIdEqualRegProcedure,
ObjectIdGetDatum(typeId2));
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)) {
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;
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);
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;
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
*/
/*
* 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);
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;
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);
relation = heap_openr(TypeRelationName);
fmgr_info(typeKey[0].sk_procedure, &typeKey[0].sk_func,
&typeKey[0].sk_nargs);
/* Delete the primary type */
/* Delete the primary type */
typeKey[0].sk_argument = PointerGetDatum(typeName);
typeKey[0].sk_argument = PointerGetDatum(typeName);
scan = heap_beginscan(relation, 0, NowTimeQual, 1, typeKey);
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))
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);
typeOid = tup->t_oid;
ItemPointerCopy(&tup->t_ctid, &itemPointerData);
heap_delete(relation, &itemPointerData);
heap_endscan(scan);
heap_close(relation);
/* Now, Delete the "array of" that type */
shadow_type = makeArrayTypeName(typeName);
typeKey[0].sk_argument = NameGetDatum(shadow_type);
scan = heap_beginscan(relation, 0, NowTimeQual,
1, (ScanKey) typeKey);
tup = heap_getnext(scan, 0, (Buffer *) 0);
if (!HeapTupleIsValid(tup))
{
elog(WARN, "RemoveType: type '%s': array stub not found",
typeName);
}
typeOid = tup->t_oid;
ItemPointerCopy(&tup->t_ctid, &itemPointerData);
heap_delete(relation, &itemPointerData);
heap_endscan(scan);
heap_close(relation);
}
/*
* RemoveFunction --
* 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;
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);
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 (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;
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);
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);
}
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);
key[0].sk_argument = PointerGetDatum(functionName);
fmgr_info(key[0].sk_procedure, &key[0].sk_func, &key[0].sk_nargs);
fmgr_info(key[0].sk_procedure, &key[0].sk_func, &key[0].sk_nargs);
relation = heap_openr(ProcedureRelationName);
scan = heap_beginscan(relation, 0, NowTimeQual, 1, key);
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])));
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)
if (!HeapTupleIsValid(tup) || namestrcmp(&(the_proc->proname),
functionName) != 0)
{
heap_endscan(scan);
heap_close(relation);
func_error("RemoveFunction", functionName,nargs, argList);
heap_endscan(scan);
heap_close(relation);
func_error("RemoveFunction", functionName, nargs, argList);
}
/* ok, function has been found */
/* ok, function has been found */
if (the_proc->prolang == INTERNALlanguageId)
elog(WARN, "RemoveFunction: function \"%s\" is built-in",
functionName);
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);
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 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 (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[0], 0x0,
Anum_pg_aggregate_aggname,
NameEqualRegProcedure,
PointerGetDatum(aggName));
ScanKeyEntryInitialize(&aggregateKey[1], 0x0,
Anum_pg_aggregate_aggbasetype,
ObjectIdEqualRegProcedure,
ObjectIdGetDatum(basetypeID));
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);
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;
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
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 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)) {
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);
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];
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;
}
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);
relrdesc = heap_openr(RelationRelationName);
oldreltup = ClassNameIndexScan(relrdesc, oldrelname);
if (!PointerIsValid(oldreltup)) {
heap_close(relrdesc);
elog(WARN, "renamerel: relation \"%s\" does not exist",
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);
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);
}

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);
}
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] = ' ';
stmt->relname = seq->seqname;
stmt->archiveLoc = -1; /* default */
stmt->archiveType = ARCH_NONE;
stmt->inhRelnames = NIL;
stmt->constraints = NIL;
ItsSequenceCreation = true; /* hack */
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';
ItsSequenceCreation = true; /* hack */
if ( WriteBuffer (buf) == STATUS_ERROR )
elog (WARN, "%s.nextval: WriteBuffer failed", elm->name);
DefineRelation(stmt);
ItemPointerSet(&iptr, 0, FirstOffsetNumber);
RelationUnsetSingleWLockPage (elm->rel, &iptr);
/*
* Xact abort calls CloseSequences, which turns ItsSequenceCreation
* off
*/
ItsSequenceCreation = false;/* hack */
return (result);
rel = heap_openr(seq->seqname);
Assert(RelationIsValid(rel));
RelationSetLockForWrite(rel);
tupDesc = RelationGetTupleDescriptor(rel);
Assert(RelationGetNumberOfBlocks(rel) == 0);
buf = ReadBuffer(rel, P_NEW);
if (!BufferIsValid(buf))
elog(WARN, "DefineSequence: ReadBuffer failed");
page = (PageHeader) BufferGetPage(buf);
PageInit((Page) page, BufferGetPageSize(buf), sizeof(sequence_magic));
sm = (sequence_magic *) PageGetSpecialPointer(page);
sm->magic = SEQ_MAGIC;
/* Now - form & insert sequence tuple */
tuple = heap_formtuple(tupDesc, value, null);
heap_insert(rel, tuple);
if (WriteBuffer(buf) == STATUS_ERROR)
elog(WARN, "DefineSequence: WriteBuffer failed");
RelationUnsetLockForWrite(rel);
heap_close(rel);
return;
}
int4
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);
/* open and WIntentLock sequence */
elm = init_sequence("nextval", 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);
}
if (elm->last != elm->cached) /* some numbers were cached */
{
elm->last += elm->increment;
return (elm->last);
}
result = elm->last;
seq = read_info("nextval", elm, &buf); /* lock page and read
* tuple */
return (result);
next = result = seq->last_value;
incby = seq->increment_by;
maxv = seq->max_value;
minv = seq->min_value;
cache = seq->cache_value;
}
if (seq->is_called != 't')
rescnt++; /* last_value if not called */
static SequenceTupleForm
read_info (char * caller, SeqTable elm, Buffer * buf)
{
ItemPointerData iptr;
PageHeader page;
ItemId lp;
HeapTuple tuple;
sequence_magic *sm;
SequenceTupleForm seq;
while (rescnt < cache) /* try to fetch cache numbers */
{
ItemPointerSet(&iptr, 0, FirstOffsetNumber);
RelationSetSingleWLockPage (elm->rel, &iptr);
/*
* Check MAXVALUE for ascending sequences and MINVALUE for
* descending sequences
*/
if (incby > 0) /* ascending sequence */
{
if ((maxv >= 0 && next > maxv - incby) ||
(maxv < 0 && next + incby > maxv))
{
if (rescnt > 0)
break; /* stop caching */
if (seq->is_cycled != 't')
elog(WARN, "%s.nextval: got MAXVALUE (%d)",
elm->name, maxv);
next = minv;
}
else
next += incby;
}
else
/* descending sequence */
{
if ((minv < 0 && next < minv - incby) ||
(minv >= 0 && next + incby < minv))
{
if (rescnt > 0)
break; /* stop caching */
if (seq->is_cycled != 't')
elog(WARN, "%s.nextval: got MINVALUE (%d)",
elm->name, minv);
next = maxv;
}
else
next += incby;
}
rescnt++; /* got result */
if (rescnt == 1) /* if it's first one - */
result = next; /* it's what to return */
}
if ( RelationGetNumberOfBlocks (elm->rel) != 1 )
elog (WARN, "%s.%s: invalid number of blocks in sequence",
elm->name, caller);
/* save info in local cache */
elm->last = result; /* last returned number */
elm->cached = next; /* last cached number */
*buf = ReadBuffer (elm->rel, 0);
if ( !BufferIsValid (*buf) )
elog (WARN, "%s.%s: ReadBuffer failed", elm->name, caller);
/* save info in sequence relation */
seq->last_value = next; /* last fetched number */
seq->is_called = 't';
page = (PageHeader) BufferGetPage (*buf);
sm = (sequence_magic *) PageGetSpecialPointer (page);
if (WriteBuffer(buf) == STATUS_ERROR)
elog(WARN, "%s.nextval: WriteBuffer failed", elm->name);
if ( sm->magic != SEQ_MAGIC )
elog (WARN, "%s.%s: bad magic (%08X)", elm->name, caller, sm->magic);
ItemPointerSet(&iptr, 0, FirstOffsetNumber);
RelationUnsetSingleWLockPage(elm->rel, &iptr);
lp = PageGetItemId (page, FirstOffsetNumber);
Assert (ItemIdIsUsed (lp));
tuple = (HeapTuple) PageGetItem ((Page) page, lp);
seq = (SequenceTupleForm) GETSTRUCT(tuple);
elm->increment = seq->increment_by;
return (seq);
return (result);
}
static SeqTable
init_sequence (char * caller, char * name)
int4
currval(struct varlena * seqin)
{
SeqTable elm, priv = (SeqTable) NULL;
SeqTable temp;
char *seqname = textout(seqin);
SeqTable elm;
int4 result;
for (elm = seqtab; elm != (SeqTable) NULL; )
{
if ( strcmp (elm->name, name) == 0 )
break;
priv = elm;
elm = elm->next;
}
/* open and WIntentLock sequence */
elm = init_sequence("currval", seqname);
pfree(seqname);
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;
}
if (elm->increment == 0) /* nextval/read_info were not called */
{
elog(WARN, "%s.currval is not yet defined in this session", elm->name);
}
temp->rel = heap_openr (name);
result = elm->last;
if ( ! RelationIsValid (temp->rel) )
elog (WARN, "%s.%s: sequence does not exist", name, caller);
return (result);
RelationSetWIntentLock (temp->rel);
}
if ( temp->rel->rd_rel->relkind != RELKIND_SEQUENCE )
elog (WARN, "%s.%s: %s is not sequence !", name, caller, name);
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 != (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;
}
ItemPointerSet(&iptr, 0, FirstOffsetNumber);
RelationSetSingleWLockPage(elm->rel, &iptr);
return (elm);
if (RelationGetNumberOfBlocks(elm->rel) != 1)
elog(WARN, "%s.%s: invalid number of blocks in sequence",
elm->name, caller);
*buf = ReadBuffer(elm->rel, 0);
if (!BufferIsValid(*buf))
elog(WARN, "%s.%s: ReadBuffer failed", elm->name, caller);
page = (PageHeader) BufferGetPage(*buf);
sm = (sequence_magic *) PageGetSpecialPointer(page);
if (sm->magic != SEQ_MAGIC)
elog(WARN, "%s.%s: bad magic (%08X)", elm->name, caller, sm->magic);
lp = PageGetItemId(page, FirstOffsetNumber);
Assert(ItemIdIsUsed(lp));
tuple = (HeapTuple) PageGetItem((Page) page, lp);
seq = (SequenceTupleForm) GETSTRUCT(tuple);
elm->increment = seq->increment_by;
return (seq);
}
static SeqTable
init_sequence(char *caller, char *name)
{
SeqTable elm,
priv = (SeqTable) NULL;
SeqTable temp;
for (elm = seqtab; elm != (SeqTable) NULL;)
{
if (strcmp(elm->name, name) == 0)
break;
priv = elm;
elm = elm->next;
}
if (elm == (SeqTable) NULL) /* not found */
{
temp = (SeqTable) malloc(sizeof(SeqTableData));
temp->name = malloc(strlen(name) + 1);
strcpy(temp->name, name);
temp->rel = (Relation) NULL;
temp->cached = temp->last = temp->increment = 0;
temp->next = (SeqTable) NULL;
}
else
/* found */
{
if (elm->rel != (Relation) NULL) /* already opened */
return (elm);
temp = elm;
}
temp->rel = heap_openr(name);
if (!RelationIsValid(temp->rel))
elog(WARN, "%s.%s: sequence does not exist", name, caller);
RelationSetWIntentLock(temp->rel);
if (temp->rel->rd_rel->relkind != RELKIND_SEQUENCE)
elog(WARN, "%s.%s: %s is not sequence !", name, caller, name);
if (elm != (SeqTable) NULL) /* we opened sequence from our */
{ /* SeqTable - check relid ! */
if (RelationGetRelationId(elm->rel) != elm->relid)
{
elog(NOTICE, "%s.%s: sequence was re-created",
name, caller, name);
elm->cached = elm->last = elm->increment = 0;
elm->relid = RelationGetRelationId(elm->rel);
}
}
else
{
elm = temp;
elm->relid = RelationGetRelationId(elm->rel);
if (seqtab == (SeqTable) NULL)
seqtab = elm;
else
priv->next = elm;
}
return (elm);
}
/*
* CloseSequences --
* 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;
SeqTable elm;
Relation rel;
ItsSequenceCreation = false;
ItsSequenceCreation = false;
for (elm = seqtab; elm != (SeqTable) NULL; )
{
if ( elm->rel != (Relation) NULL ) /* opened in current xact */
for (elm = seqtab; elm != (SeqTable) NULL;)
{
rel = elm->rel;
elm->rel = (Relation) NULL;
RelationUnsetWIntentLock (rel);
heap_close (rel);
}
elm = elm->next;
}
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)
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;
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);
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);
}
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 (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 */
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);
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);
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 */
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));
if (nodeTag(def->arg) == T_Integer)
return (intVal(def->arg));
elog (WARN, "DefineSequence: \"%s\" is to be integer", def->defname);
return (-1);
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,68 +42,73 @@
*---------------------------------------------------------------------
*/
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;
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;
/*
* 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;
/*
* find the names of the attribute & its type
*/
entry = lfirst(t);
res = entry->resdom;
resname = res->resname;
restypename = tname(get_id_type(res->restype));
/*
* 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 = makeNode(TypeName);
typename = makeNode(TypeName);
typename->name = pstrdup(restypename);
def->colname = pstrdup(resname);
typename->name = pstrdup(restypename);
def->colname = pstrdup(resname);
def->typename = typename;
def->typename = typename;
def->is_not_null = false;
def->defval = (char*) NULL;
def->is_not_null = false;
def->defval = (char *) NULL;
attrList = lappend(attrList, def);
attrList = lappend(attrList, def);
}
}
else
{
elog(WARN, "attempted to define virtual relation with no attrs");
}
} else {
elog ( WARN, "attempted to define virtual relation with no attrs");
}
/*
* now create the parametesr for keys/inheritance etc.
* 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;
/*
* 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);
/*
* finally create the relation...
*/
DefineRelation(&createStmt);
}
/*------------------------------------------------------------------
@ -118,81 +123,84 @@ 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;
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);
/*
* 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);
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;
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);
RuleStmt *retrieve_rule = NULL;
#ifdef NOTYET
replace_rule =
FormViewReplaceRule(viewName, viewParse);
append_rule =
FormViewAppendRule(viewName, viewParse);
delete_rule =
FormViewDeleteRule(viewName, viewParse);
RuleStmt *replace_rule = NULL;
RuleStmt *append_rule = NULL;
RuleStmt *delete_rule = NULL;
#endif
DefineQueryRewrite(retrieve_rule);
retrieve_rule =
FormViewRetrieveRule(viewName, viewParse);
#ifdef NOTYET
DefineQueryRewrite(replace_rule);
DefineQueryRewrite(append_rule);
DefineQueryRewrite(delete_rule);
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
}
@ -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;
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);
/*
* 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;
/*
* 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);
/*
* 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;
/*
* 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;
viewTlist = viewParse->targetList;
/*
* Create the "view" relation
* NOTE: if it already exists, the xaxt will be aborted.
*/
DefineVirtualRelation(viewName, viewTlist);
/*
* 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 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);
/*
* 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;
char *rname;
/*
* first remove all the "view" rules...
* Currently we only have one!
*/
rname = MakeRetrieveViewRuleName(viewName);
RemoveRewriteRule(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();
/*
* we don't really need that, but just in case...
*/
CommandCounterIncrement();
/*
* now remove the relation.
*/
heap_destroy(viewName);
pfree(rname);
/*
* 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;
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);
/* ----------------
* 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;
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);
relation = (Relation) NULL;
if (relation == NULL)
elog(DEBUG, "ExecOpenR: relation == NULL, heap_open failed.");
/* ----------------
* open the relation with the correct call depending
* on whether this is a heap relation or an index relation.
* ----------------
*/
if (isindex)
{
relation = index_open(relationOid);
}
else
relation = heap_open(relationOid);
return relation;
if (relation == NULL)
elog(DEBUG, "ExecOpenR: relation == NULL, heap_open failed.");
return relation;
}
/* ----------------------------------------------------------------
* ExecBeginScan
* ExecBeginScan
*
* beginscans a relation in current direction.
* beginscans a relation in current direction.
*
* XXX fix parameters to AMbeginscan (and btbeginscan)
* 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;
Pointer scanDesc;
scanDesc = NULL;
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);
}
/* ----------------
* 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.");
if (scanDesc == NULL)
elog(DEBUG, "ExecBeginScan: scanDesc = NULL, heap_beginscan failed.");
return scanDesc;
return scanDesc;
}
/* ----------------------------------------------------------------
* ExecCloseR
* ExecCloseR
*
* closes the relation and scan descriptor for a scan or sort
* 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;
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;
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 */
if (scanDesc != NULL)
heap_rescan(scanDesc, /* scan desc */
ScanDirectionIsBackward(direction), /* backward flag */
skeys); /* scan keys */
return scanDesc;
return scanDesc;
}
/* ----------------------------------------------------------------
* ExecMarkPos
* ExecMarkPos
*
* Marks the current scan position.
* Marks the current scan position.
*
* XXX Needs to be extended to include all the node types.
* XXX Needs to be extended to include all the node types.
* ----------------------------------------------------------------
*/
void
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;
switch (nodeTag(node))
{
case T_SeqScan:
ExecSeqRestrPos((SeqScan *) node);
return;
case T_IndexScan:
ExecIndexRestrPos((IndexScan *) node);
return;
case T_IndexScan:
ExecIndexRestrPos((IndexScan *) node);
return;
case T_Sort:
ExecSortRestrPos((Sort *) node);
return;
case T_Sort:
ExecSortRestrPos((Sort *) node);
return;
default:
/* elog(DEBUG, "ExecRestrPos: node type not supported"); */
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);
EU3_printf("ExecCreatR: %s type=%d oid=%d\n",
"entering: ", tupType, relationOid);
CXT1_printf("ExecCreatR: context is %d\n", CurrentMemoryContext);
relDesc = NULL;
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)
* ----------------
*/
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.");
/*
* heap_creatr creates a name if the argument to heap_creatr is
* '\0 '
*/
relDesc = heap_creatr("",
DEFAULT_SMGR,
tupType);
}
else
{
/* ----------------
* use a relation from the range table
* ----------------
*/
elog(DEBUG, "ExecCreatR: %s",
"stuff using range table id's is not functional");
}
EU1_printf("ExecCreatR: returning relDesc=%d\n", relDesc);
if (relDesc == NULL)
elog(DEBUG, "ExecCreatR: failed to create relation.");
return relDesc;
EU1_printf("ExecCreatR: returning relDesc=%d\n", relDesc);
return relDesc;
}

View File

@ -1,29 +1,29 @@
/*-------------------------------------------------------------------------
*
* execFlatten.c--
* 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;
Node *expression;
expression = iterNode->iterexpr;
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);
/*
* 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;
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)
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);
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;
resVect[curNode] = ExecEvalIter((Iter *) tle->expr,
econtext,
&isNullVect[curNode],
&isDone);
if (isDone)
isNullVect[curNode] = alwaysDone[curNode] = true;
else
alwaysDone[curNode] = false;
curNode++;
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;
/*
* 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;
/*
* 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;
/*
* 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 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.
*/
if (alwaysDone[0] == true)
/*
* Fill in nulls for every function that is always done.
*/
for (curNode = fjNode->fj_nNodes - 1; curNode >= 0; curNode--)
isNullVect[curNode] = alwaysDone[curNode];
if (alwaysDone[0] == true)
{
*fj_isDone = FjoinBumpOuterNodes(tlist,
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;
*fj_isDone = FjoinBumpOuterNodes(tlist,
econtext,
resVect,
isNullVect);
if (*fj_isDone)
return;
resVect[0] = ExecEvalIter((Iter*)fjNode->fj_innerNode->expr,
econtext,
&isNullVect[0],
&isDone);
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;
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))
/*
* 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);
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 (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);
TargetEntry *tle = lfirst(trailers);
if (alwaysDone[trailNode] != true)
results[trailNode] = ExecEvalIter((Iter)tle->expr,
econtext,
&nulls[trailNode],
&funcIsDone);
trailNode++;
trailers = lnext(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,12 +20,12 @@
#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
@ -60,173 +60,195 @@
* 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);
/* ---------------------
* 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);
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;
len = ExecTargetListLength(targetList);
cleanLength = ExecTargetListLength(cleanTargetList);
return(junkfilter);
/* ---------------------
* Now calculate the "map" between the original tuples attributes
* and the "clean" tuple's attributes.
*
* The "map" is an array of "cleanLength" attribute numbers, i.e.
* one entry for every attribute of the "clean" tuple.
* The value of this entry is the attribute number of the corresponding
* attribute of the "original" tuple.
* ---------------------
*/
if (cleanLength > 0)
{
size = cleanLength * sizeof(AttrNumber);
cleanMap = (AttrNumber *) palloc(size);
cleanResno = 1;
foreach(t, targetList)
{
TargetEntry *tle = lfirst(t);
if (tle->resdom != NULL)
{
resdom = tle->resdom;
expr = tle->expr;
resjunk = resdom->resjunk;
if (resjunk == 0)
{
cleanMap[cleanResno - 1] = resdom->resno;
cleanResno++;
}
}
else
{
#ifdef SETS_FIXED
List fjListP;
List fjList = lfirst(t);
Fjoin fjNode = (Fjoin) lfirst(fjList);
/* what the hell is this????? */
resdom = (Resdom) lfirst(get_fj_innerNode(fjNode));
#endif
cleanMap[cleanResno - 1] = tle->resdom->resno;
cleanResno++;
#ifdef SETS_FIXED
foreach(fjListP, lnext(fjList))
{
TargetEntry *tle = lfirst(fjListP);
resdom = tle->resdom;
cleanMap[cleanResno - 1] = resdom->resno;
cleanResno++;
}
#endif
}
}
}
else
{
cleanMap = NULL;
}
/* ---------------------
* Finally create and initialize the JunkFilter.
* ---------------------
*/
junkfilter = makeNode(JunkFilter);
junkfilter->jf_targetList = targetList;
junkfilter->jf_length = len;
junkfilter->jf_tupType = tupType;
junkfilter->jf_cleanTargetList = cleanTargetList;
junkfilter->jf_cleanLength = cleanLength;
junkfilter->jf_cleanTupType = cleanTupType;
junkfilter->jf_cleanMap = cleanMap;
return (junkfilter);
}
@ -242,57 +264,61 @@ ExecInitJunkFilter(List *targetList)
*-------------------------------------------------------------------------
*/
bool
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;
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;
/* ---------------------
* 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;
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);
}
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;
/* ---------------------
* 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);
*value = (Datum)
heap_getattr(tuple, InvalidBuffer, resno, tupType, isNull);
return true;
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];
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;
/* ----------------
* 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;
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;
/* ---------------------
* 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);
/* ---------------------
* 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';
size = cleanLength * sizeof(char);
nulls = (char *) palloc(size);
}
else
nulls[i] = ' ';
}
{
values = values_array;
nulls = nulls_array;
}
/* ---------------------
* Now form the new tuple.
* ---------------------
*/
cleanTuple = heap_formtuple(cleanTupType,
values,
nulls);
/* ---------------------
* 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);
/* ---------------------
* We are done. Free any space allocated for 'values' and 'nulls'
* and return the new tuple.
* ---------------------
*/
if (cleanLength > 64) {
pfree(values);
pfree(nulls);
}
values[i] = d;
return(cleanTuple);
if (isNull)
nulls[i] = 'n';
else
nulls[i] = ' ';
}
/* ---------------------
* Now form the new tuple.
* ---------------------
*/
cleanTuple = heap_formtuple(cleanTupType,
values,
nulls);
/* ---------------------
* We are done. Free any space allocated for 'values' and 'nulls'
* and return the new tuple.
* ---------------------
*/
if (cleanLength > 64)
{
pfree(values);
pfree(nulls);
}
return (cleanTuple);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,75 +1,75 @@
/*-------------------------------------------------------------------------
*
* execProcnode.c--
* 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.
* 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:
* 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:
*
* retrieve (DEPT.no_emps, EMP.age)
* where EMP.name = DEPT.mgr and
* DEPT.name = "shoe"
* retrieve (DEPT.no_emps, EMP.age)
* where EMP.name = DEPT.mgr and
* DEPT.name = "shoe"
*
* Suppose the planner gives us the following plan:
* Suppose the planner gives us the following plan:
*
* Nest Loop (DEPT.mgr = EMP.name)
* / \
* / \
* Seq Scan Seq Scan
* DEPT EMP
* (name = "shoe")
* 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.
* 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.
* * 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.
* * 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().
* * 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.
* 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
* ExecInitNode
*
* Recursively initializes all the nodes in the plan rooted
* at 'node'.
* Recursively initializes all the nodes in the plan rooted
* at 'node'.
*
* Initial States:
* 'node' is the plan produced by the query planner
* Initial States:
* 'node' is the plan produced by the query planner
*
* returns TRUE/FALSE on whether the plan was successfully initialized
* returns TRUE/FALSE on whether the plan was successfully initialized
* ------------------------------------------------------------------------
*/
bool
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;
bool result;
/* ----------------
* scan nodes
* do nothing when we get to the end
* of a leaf on tree.
* ----------------
*/
case T_SeqScan:
result = ExecInitSeqScan((SeqScan *)node, estate, parent);
break;
if (node == NULL)
return FALSE;
case T_IndexScan:
result = ExecInitIndexScan((IndexScan *)node, estate, parent);
break;
switch (nodeTag(node))
{
/* ----------------
* control nodes
* ----------------
*/
case T_Result:
result = ExecInitResult((Result *) node, estate, parent);
break;
/* ----------------
* join nodes
* ----------------
*/
case T_NestLoop:
result = ExecInitNestLoop((NestLoop *)node, estate, parent);
break;
case T_Append:
result = ExecInitAppend((Append *) node, estate, parent);
break;
case T_MergeJoin:
result = ExecInitMergeJoin((MergeJoin *)node, estate, parent);
break;
/* ----------------
* scan nodes
* ----------------
*/
case T_SeqScan:
result = ExecInitSeqScan((SeqScan *) node, estate, parent);
break;
/* ----------------
* materialization nodes
* ----------------
*/
case T_Material:
result = ExecInitMaterial((Material *)node, estate, parent);
break;
case T_IndexScan:
result = ExecInitIndexScan((IndexScan *) node, estate, parent);
break;
case T_Sort:
result = ExecInitSort((Sort *)node, estate, parent);
break;
/* ----------------
* join nodes
* ----------------
*/
case T_NestLoop:
result = ExecInitNestLoop((NestLoop *) node, estate, parent);
break;
case T_Unique:
result = ExecInitUnique((Unique *)node, estate, parent);
break;
case T_MergeJoin:
result = ExecInitMergeJoin((MergeJoin *) node, estate, parent);
break;
case T_Group:
result = ExecInitGroup((Group *)node, estate, parent);
break;
/* ----------------
* materialization nodes
* ----------------
*/
case T_Material:
result = ExecInitMaterial((Material *) node, estate, parent);
break;
case T_Agg:
result = ExecInitAgg((Agg *)node, estate, parent);
break;
case T_Sort:
result = ExecInitSort((Sort *) node, estate, parent);
break;
case T_Hash:
result = ExecInitHash((Hash *)node, estate, parent);
break;
case T_Unique:
result = ExecInitUnique((Unique *) node, estate, parent);
break;
case T_HashJoin:
result = ExecInitHashJoin((HashJoin *)node, estate, parent);
break;
case T_Group:
result = ExecInitGroup((Group *) node, estate, parent);
break;
case T_Tee:
result = ExecInitTee((Tee*)node, estate, parent);
break;
case T_Agg:
result = ExecInitAgg((Agg *) node, estate, parent);
break;
default:
elog(DEBUG, "ExecInitNode: node not yet supported: %d",
nodeTag(node));
result = FALSE;
}
case T_Hash:
result = ExecInitHash((Hash *) node, estate, parent);
break;
return result;
case T_HashJoin:
result = ExecInitHashJoin((HashJoin *) node, estate, parent);
break;
case T_Tee:
result = ExecInitTee((Tee *) node, estate, parent);
break;
default:
elog(DEBUG, "ExecInitNode: node not yet supported: %d",
nodeTag(node));
result = FALSE;
}
return result;
}
/* ----------------------------------------------------------------
* ExecProcNode
* ExecProcNode
*
* Initial States:
* the query tree must be initialized once by calling ExecInit.
* 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;
TupleTableSlot *result;
/* ----------------
* scan nodes
* deal with NULL nodes..
* ----------------
*/
case T_SeqScan:
result = ExecSeqScan((SeqScan *)node);
break;
if (node == NULL)
return NULL;
case T_IndexScan:
result = ExecIndexScan((IndexScan *)node);
break;
switch (nodeTag(node))
{
/* ----------------
* control nodes
* ----------------
*/
case T_Result:
result = ExecResult((Result *) node);
break;
/* ----------------
* join nodes
* ----------------
*/
case T_NestLoop:
result = ExecNestLoop((NestLoop *)node, parent);
break;
case T_Append:
result = ExecProcAppend((Append *) node);
break;
case T_MergeJoin:
result = ExecMergeJoin((MergeJoin *)node);
break;
/* ----------------
* scan nodes
* ----------------
*/
case T_SeqScan:
result = ExecSeqScan((SeqScan *) node);
break;
/* ----------------
* materialization nodes
* ----------------
*/
case T_Material:
result = ExecMaterial((Material *)node);
break;
case T_IndexScan:
result = ExecIndexScan((IndexScan *) node);
break;
case T_Sort:
result = ExecSort((Sort *)node);
break;
/* ----------------
* join nodes
* ----------------
*/
case T_NestLoop:
result = ExecNestLoop((NestLoop *) node, parent);
break;
case T_Unique:
result = ExecUnique((Unique *)node);
break;
case T_MergeJoin:
result = ExecMergeJoin((MergeJoin *) node);
break;
case T_Group:
result = ExecGroup((Group *)node);
break;
/* ----------------
* materialization nodes
* ----------------
*/
case T_Material:
result = ExecMaterial((Material *) node);
break;
case T_Agg:
result = ExecAgg((Agg *)node);
break;
case T_Sort:
result = ExecSort((Sort *) node);
break;
case T_Hash:
result = ExecHash((Hash *)node);
break;
case T_Unique:
result = ExecUnique((Unique *) node);
break;
case T_HashJoin:
result = ExecHashJoin((HashJoin *)node);
break;
case T_Group:
result = ExecGroup((Group *) node);
break;
case T_Tee:
result = ExecTee((Tee*)node, parent);
break;
case T_Agg:
result = ExecAgg((Agg *) node);
break;
default:
elog(DEBUG, "ExecProcNode: node not yet supported: %d",
nodeTag(node));
result = FALSE;
}
case T_Hash:
result = ExecHash((Hash *) node);
break;
return result;
case T_HashJoin:
result = ExecHashJoin((HashJoin *) node);
break;
case T_Tee:
result = ExecTee((Tee *) node, parent);
break;
default:
elog(DEBUG, "ExecProcNode: node not yet supported: %d",
nodeTag(node));
result = FALSE;
}
return result;
}
int
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
* ExecEndNode
*
* Recursively cleans up all the nodes in the plan rooted
* at 'node'.
* 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.
* 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;
if (node == NULL)
return;
case T_Append:
ExecEndAppend((Append *)node);
break;
switch (nodeTag(node))
{
/* ----------------
* control nodes
* ----------------
*/
case T_Result:
ExecEndResult((Result *) node);
break;
/* ----------------
* scan nodes
* ----------------
*/
case T_SeqScan:
ExecEndSeqScan((SeqScan *)node);
break;
case T_Append:
ExecEndAppend((Append *) node);
break;
case T_IndexScan:
ExecEndIndexScan((IndexScan *)node);
break;
/* ----------------
* scan nodes
* ----------------
*/
case T_SeqScan:
ExecEndSeqScan((SeqScan *) node);
break;
/* ----------------
* join nodes
* ----------------
*/
case T_NestLoop:
ExecEndNestLoop((NestLoop *)node);
break;
case T_IndexScan:
ExecEndIndexScan((IndexScan *) node);
break;
case T_MergeJoin:
ExecEndMergeJoin((MergeJoin *)node);
break;
/* ----------------
* join nodes
* ----------------
*/
case T_NestLoop:
ExecEndNestLoop((NestLoop *) node);
break;
/* ----------------
* materialization nodes
* ----------------
*/
case T_Material:
ExecEndMaterial((Material *)node);
break;
case T_MergeJoin:
ExecEndMergeJoin((MergeJoin *) node);
break;
case T_Sort:
ExecEndSort((Sort *)node);
break;
/* ----------------
* materialization nodes
* ----------------
*/
case T_Material:
ExecEndMaterial((Material *) node);
break;
case T_Unique:
ExecEndUnique((Unique *)node);
break;
case T_Sort:
ExecEndSort((Sort *) node);
break;
case T_Group:
ExecEndGroup((Group *)node);
break;
case T_Unique:
ExecEndUnique((Unique *) node);
break;
case T_Agg:
ExecEndAgg((Agg *)node);
break;
case T_Group:
ExecEndGroup((Group *) node);
break;
/* ----------------
* XXX add hooks to these
* ----------------
*/
case T_Hash:
ExecEndHash((Hash *) node);
break;
case T_Agg:
ExecEndAgg((Agg *) node);
break;
case T_HashJoin:
ExecEndHashJoin((HashJoin *) node);
break;
/* ----------------
* XXX add hooks to these
* ----------------
*/
case T_Hash:
ExecEndHash((Hash *) node);
break;
case T_Tee:
ExecEndTee((Tee*) node, parent);
break;
case T_HashJoin:
ExecEndHashJoin((HashJoin *) node);
break;
default:
elog(DEBUG, "ExecEndNode: node not yet supported",
nodeTag(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
* 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.
* 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.
* 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.
* 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.
* 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;
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;
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;
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;
execution_state *newes;
execution_state *nextes;
execution_state *preves;
QueryTreeList *queryTree_list;
int i;
List *planTree_list;
int nargs;
nargs = fcache->nargs;
nargs = fcache->nargs;
newes = (execution_state *) palloc(sizeof(execution_state));
nextes = newes;
preves = (execution_state *)NULL;
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);
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);
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;
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();
nextes->next = NULL;
nextes->status = F_EXEC_START;
nextes->qd = CreateQueryDesc(queryTree,
planTree,
None);
estate = CreateExecutorState();
if (nargs > 0) {
int i;
ParamListInfo paramLI;
if (nargs > 0)
{
int i;
ParamListInfo paramLI;
paramLI =
(ParamListInfo)palloc((nargs+1)*sizeof(ParamListInfoData));
paramLI =
(ParamListInfo) palloc((nargs + 1) * sizeof(ParamListInfoData));
memset(paramLI, 0, nargs*sizeof(ParamListInfoData));
memset(paramLI, 0, nargs * sizeof(ParamListInfoData));
estate->es_param_list_info = paramLI;
estate->es_param_list_info = paramLI;
for (i=0; i<nargs; paramLI++, i++) {
paramLI->kind = PARAM_NUM;
paramLI->id = i+1;
paramLI->isnull = false;
paramLI->value = (Datum) NULL;
}
paramLI->kind = PARAM_INVALID;
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;
TupleTableSlot *funcSlot;
TupleDesc resultTd;
HeapTuple newTuple;
HeapTuple oldTuple;
Assert(! TupIsNull(resultSlot));
oldTuple = resultSlot->val;
Assert(!TupIsNull(resultSlot));
oldTuple = resultSlot->val;
funcSlot = (TupleTableSlot*)fcache->funcSlot;
funcSlot = (TupleTableSlot *) fcache->funcSlot;
if (funcSlot == (TupleTableSlot*)NULL)
return resultSlot;
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;
resultTd = resultSlot->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++;
/*
* 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);
newTuple = heap_copytuple(oldTuple);
return ExecStoreTuple(newTuple,funcSlot,InvalidBuffer,true);
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);
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 (TupIsNull(slot))
{
postquel_end(es);
es->status = F_EXEC_DONE;
*isNull = true;
if (LAST_POSTQUEL_COMMAND(es)) {
TupleTableSlot *resSlot;
/*
* If this isn't the last command for the function we have to
* increment the command counter so that subsequent commands can
* see changes made by previous ones.
*/
if (!LAST_POSTQUEL_COMMAND(es))
CommandCounterIncrement();
return (Datum) NULL;
}
/*
* 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);
if (LAST_POSTQUEL_COMMAND(es))
{
TupleTableSlot *resSlot;
tup = resSlot->val;
value = ProjectAttribute(resSlot->ttc_tupleDescriptor,
tle,
tup,
isNull);
}else {
value = (Datum)resSlot;
*isNull = false;
/*
* 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;
}
/*
* If this is a single valued function we have to end the
* function execution now.
* 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.
*/
if (fcache->oneResult) {
postquel_end(es);
es->status = F_EXEC_DONE;
}
return value;
}
/*
* If this isn't the last command for the function, we don't
* return any results, but we have to increment the command
* counter so that subsequent commands can see changes made
* by previous ones.
*/
CommandCounterIncrement();
return (Datum)NULL;
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;
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)
{
/*
* 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)
{
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;
savedId = GetScanCommandId();
SetScanCommandId(GetCurrentCommandId());
SetScanCommandId (savedId);
return result;
es = (execution_state *) fcache->func_state;
if (es == NULL)
{
es = init_execution_state(fcache, args);
fcache->func_state = (char *) es;
}
while (es && es->status == F_EXEC_DONE)
es = es->next;
Assert(es);
/*
* Execute each command in the function one after another until we're
* executing the final command and get a result or we run out of
* commands.
*/
while (es != (execution_state *) NULL)
{
result = postquel_execute(es,
fcache,
funcNode->func_tlist,
args,
isNull);
if (es->status != F_EXEC_DONE)
break;
es = es->next;
}
/*
* If we've gone through every command in this function, we are done.
*/
if (es == (execution_state *) NULL)
{
/*
* Reset the execution states to start over again
*/
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
* exec-append-initialize-next
*
* Sets up the append node state (i.e. the append state node)
* for the "next" scan.
* 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.
* 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;
EState *estate;
AppendState *unionstate;
TupleTableSlot *result_slot;
List *rangeTable;
int whichplan;
int nplans;
List *rtentries;
ResTarget *rtentry;
int whichplan;
int nplans;
List *rtentries;
ResTarget *rtentry;
Index unionrelid;
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;
/* ----------------
* 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;
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 (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;
rt_store(unionrelid, rangeTable, rtentry);
if (unionstate->as_junkFilter_list) {
estate->es_junkFilter =
(JunkFilter*)nth(whichplan,
unionstate->as_junkFilter_list);
}
if (unionstate->as_result_relation_info_list) {
estate->es_result_relation_info =
(RelationInfo*) nth(whichplan,
unionstate->as_result_relation_info_list);
}
result_slot->ttc_whichplan = whichplan;
}
else if (whichplan >= nplans)
{
/* ----------------
* as above, end the scan if we go beyond
* the last scan in our list..
* ----------------
*/
unionstate->as_whichplan = nplans - 1;
return FALSE;
return TRUE;
}
}
else
{
/* ----------------
* initialize the scan
* (and update the range table appropriately)
* (doesn't this leave the range table hosed for anybody upstream
* of the Append node??? - jolly )
* ----------------
*/
if (node->unionrelid > 0)
{
rtentry = nth(whichplan, rtentries);
if (rtentry == NULL)
elog(DEBUG, "exec_append_initialize_next: rtentry is nil");
unionrelid = node->unionrelid;
rt_store(unionrelid, rangeTable, rtentry);
if (unionstate->as_junkFilter_list)
{
estate->es_junkFilter =
(JunkFilter *) nth(whichplan,
unionstate->as_junkFilter_list);
}
if (unionstate->as_result_relation_info_list)
{
estate->es_result_relation_info =
(RelationInfo *) nth(whichplan,
unionstate->as_result_relation_info_list);
}
result_slot->ttc_whichplan = whichplan;
}
return TRUE;
}
}
/* ----------------------------------------------------------------
* ExecInitAppend
* ExecInitAppend
*
* Begins all of the subscans of the append node, storing the
* scan structures in the 'initialized' vector of the append-state
* structure.
* 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.)
* (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.
* 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;
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;
/* ----------------
* assign execution state to node and get information
* for append state
* ----------------
*/
node->plan.state = estate;
unionplans = node->unionplans;
nplans = length(unionplans);
rtentries = node->unionrtentries;
unionplans = node->unionplans;
nplans = length(unionplans);
rtentries = node->unionrtentries;
CXT1_printf("ExecInitAppend: context is %d\n", CurrentMemoryContext);
initialized = (bool *)palloc(nplans * sizeof(bool));
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;
/* ----------------
* 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;
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);
/* ----------------
* 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
* ---------------
/* ----------------
* append nodes still have Result slots, which hold pointers
* to tuples, so we have to initialize them..
* ----------------
*/
if ((es_rri != (RelationInfo*)NULL) &&
(node->unionrelid == es_rri->ri_RangeTableIndex)) {
ExecInitResultTupleSlot(estate, &unionstate->cstate);
targetList = initNode->targetlist;
j = (JunkFilter *) ExecInitJunkFilter(targetList);
junkList = lappend(junkList, j);
/*
* 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;
}
unionstate->as_junkFilter_list = junkList;
if (junkList != NIL)
estate->es_junkFilter = (JunkFilter *)lfirst(junkList);
for (i = 0; i < nplans; i++)
{
JunkFilter *j;
List *targetList;
/* ----------------
* 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;
/* ----------------
* 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);
/* ----------------
* return the result from the first subplan's initialization
* ----------------
*/
unionstate->as_whichplan = 0;
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];
result = (List *) initialized[0];
#endif
return TRUE;
return TRUE;
}
int
ExecCountSlotsAppend(Append *node)
ExecCountSlotsAppend(Append * node)
{
List *plan;
List *unionplans = node->unionplans;
int nSlots = 0;
List *plan;
List *unionplans = node->unionplans;
int nSlots = 0;
foreach (plan,unionplans) {
nSlots += ExecCountSlotsNode((Plan *)lfirst(plan));
}
return nSlots + APPEND_NSLOTS;
foreach(plan, unionplans)
{
nSlots += ExecCountSlotsNode((Plan *) lfirst(plan));
}
return nSlots + APPEND_NSLOTS;
}
/* ----------------------------------------------------------------
* ExecProcAppend
* ExecProcAppend
*
* Handles the iteration over the multiple scans.
* Handles the iteration over the multiple scans.
*
* NOTE: Can't call this ExecAppend, that name is used in execMain.l
* NOTE: Can't call this ExecAppend, that name is used in execMain.l
* ----------------------------------------------------------------
*/
TupleTableSlot *
ExecProcAppend(Append *node)
ExecProcAppend(Append * node)
{
EState *estate;
AppendState *unionstate;
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;
}
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
* ExecEndAppend
*
* Shuts down the subscans of the append node.
* Shuts down the subscans of the append node.
*
* Returns nothing of interest.
* Returns nothing of interest.
* ----------------------------------------------------------------
*/
void
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;
/* ----------------
* 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 );
}
}
/* ----------------
* 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;
/* ----------------
* 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);
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 */
/*
* 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
*
* 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;
GroupState *grpstate;
Plan *outerPlan;
/*
* assign the node's execution state
*/
node->plan.state = estate;
/*
* 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;
/*
* 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);
/*
* tuple table initialization
*/
ExecInitScanTupleSlot(estate, &grpstate->csstate);
ExecInitResultTupleSlot(estate, &grpstate->csstate.cstate);
/* ----------------
* initialize tuple type.
* ----------------
*/
ExecAssignScanTypeFromOuterPlan((Plan *) node, &grpstate->csstate);
/*
* initializes child nodes
*/
outerPlan = outerPlan(node);
ExecInitNode(outerPlan, estate, (Plan *) node);
/*
* 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);
/* ----------------
* initialize tuple type.
* ----------------
*/
ExecAssignScanTypeFromOuterPlan((Plan *) node, &grpstate->csstate);
return TRUE;
/*
* Initialize tuple type for both result and scan. This node does no
* projection
*/
ExecAssignResultTypeFromTL((Plan *) node, &grpstate->csstate.cstate);
ExecAssignProjectionInfo((Plan *) node, &grpstate->csstate.cstate);
return TRUE;
}
int
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);
outerPlan = outerPlan(node);
ExecEndNode(outerPlan, (Plan *) node);
/* clean up tuple table */
ExecClearTuple(grpstate->csstate.css_ScanTupleSlot);
/* 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);
attr2 = heap_getattr(newslot->val,
InvalidBuffer,
att,
tupdesc,
&isNull2);
if (isNull1 == isNull2) {
if (isNull1) /* both are null, they are equal */
continue;
if (isNull1 == isNull2)
{
if (isNull1) /* both are null, they are equal */
continue;
val1 = fmgr(typoutput, attr1,
gettypelem(tupdesc->attrs[att-1]->atttypid));
val2 = fmgr(typoutput, attr2,
gettypelem(tupdesc->attrs[att-1]->atttypid));
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;
/*
* 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;
EState *estate;
MaterialState *matstate;
Plan *outerNode;
ScanDirection dir;
Relation tempRelation;
Relation currentRelation;
HeapScanDesc currentScanDesc;
HeapTuple heapTuple;
TupleTableSlot *slot;
Buffer buffer;
/* ----------------
* if we couldn't create the temp or current relations then
* we print a warning and return NULL.
* get state info from node
* ----------------
*/
tempRelation = matstate->mat_TempRelation;
if (tempRelation == NULL) {
elog(DEBUG, "ExecMaterial: temp relation is NULL! aborting...");
return NULL;
}
matstate = node->matstate;
estate = node->plan.state;
dir = estate->es_direction;
currentRelation = matstate->csstate.css_currentRelation;
if (currentRelation == NULL) {
elog(DEBUG, "ExecMaterial: current relation is NULL! aborting...");
return NULL;
/* ----------------
* 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;
}
/* ----------------
* retrieve tuples from the subplan and
* insert them in the temporary relation
* at this point we know we have a sorted relation so
* we preform a simple scan on it with amgetnext()..
* ----------------
*/
outerNode = outerPlan((Plan *) node);
for (;;) {
slot = ExecProcNode(outerNode, (Plan*) node);
currentScanDesc = matstate->csstate.css_currentScanDesc;
heapTuple = slot->val;
if (heapTuple == NULL)
break;
heap_insert(tempRelation, /* relation desc */
heapTuple); /* heap tuple to insert */
ExecClearTuple( slot);
}
currentRelation = tempRelation;
heapTuple = heap_getnext(currentScanDesc, /* scan desc */
ScanDirectionIsBackward(dir),
/* bkwd flag */
&buffer); /* return: buffer */
/* ----------------
* restore to user specified direction
* put the tuple into the scan tuple slot and return the slot.
* Note: since the tuple is really a pointer to a page, we don't want
* to call pfree() on it..
* ----------------
*/
estate->es_direction = dir;
slot = (TupleTableSlot *) matstate->csstate.css_ScanTupleSlot;
/* ----------------
* 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 */
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; */
MaterialState *matstate;
Plan *outerPlan;
TupleDesc tupType;
Relation tempDesc;
/* ----------------
* assign the node's execution state
* ----------------
*/
node->plan.state = estate;
/* int len; */
/* ----------------
* create state structure
* ----------------
*/
matstate = makeNode(MaterialState);
matstate->mat_Flag = false;
matstate->mat_TempRelation = NULL;
node->matstate = matstate;
/* ----------------
* assign the node's execution state
* ----------------
*/
node->plan.state = estate;
/* ----------------
* 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);
/* ----------------
* 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);
/* ----------------
* tuple table initialization
* ----------------
*/
ExecInitScanTupleSlot(estate, &matstate->csstate);
/* ----------------
* initializes child nodes
* ----------------
*/
outerPlan = outerPlan((Plan *) node);
ExecInitNode(outerPlan, estate, (Plan *) node);
/* ----------------
* initializes child nodes
* ----------------
*/
outerPlan = outerPlan((Plan *) node);
ExecInitNode(outerPlan, estate, (Plan *) node);
/* ----------------
* initialize matstate information
* ----------------
*/
matstate->mat_Flag = false;
/* ----------------
* 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;
/* ----------------
* 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);
/* ----------------
* 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_);
/* ----------------
* 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;
/* ----------------
* 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;
/* ----------------
* 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;
MaterialState *matstate;
Relation tempRelation;
Plan *outerPlan;
/* ----------------
* get info from the material state
* ----------------
*/
matstate = node->matstate;
tempRelation = matstate->mat_TempRelation;
/* ----------------
* get info from the material state
* ----------------
*/
matstate = node->matstate;
tempRelation = matstate->mat_TempRelation;
heap_destroyr(tempRelation);
heap_destroyr(tempRelation);
/* ----------------
* close the temp relation and shut down the scan.
* ----------------
*/
ExecCloseR((Plan *) node);
/* ----------------
* close the temp relation and shut down the scan.
* ----------------
*/
ExecCloseR((Plan *) node);
/* ----------------
* shut down the subplan
* ----------------
*/
outerPlan = outerPlan((Plan *) node);
ExecEndNode(outerPlan, (Plan*) node);
/* ----------------
* shut down the subplan
* ----------------
*/
outerPlan = outerPlan((Plan *) node);
ExecEndNode(outerPlan, (Plan *) node);
/* ----------------
* clean out the tuple table
* ----------------
*/
ExecClearTuple(matstate->csstate.css_ScanTupleSlot);
/* ----------------
* clean out the tuple table
* ----------------
*/
ExecClearTuple(matstate->csstate.css_ScanTupleSlot);
}
#ifdef NOT_USED /* not used */
#ifdef NOT_USED /* not used */
/* ----------------------------------------------------------------
* ExecMaterialMarkPos
* ExecMaterialMarkPos
* ----------------------------------------------------------------
*/
List /* nothing of interest */
List /* nothing of interest */
ExecMaterialMarkPos(Material node)
{
MaterialState matstate;
HeapScanDesc sdesc;
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);
/* ----------------
* 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;
}
/* ----------------------------------------------------------------
* ExecMaterialRestrPos
* ExecMaterialRestrPos
* ----------------------------------------------------------------
*/
void
ExecMaterialRestrPos(Material node)
{
MaterialState matstate;
HeapScanDesc sdesc;
MaterialState matstate;
HeapScanDesc sdesc;
/* ----------------
* if we haven't materialized yet, just return.
* ----------------
*/
matstate = get_matstate(node);
if (get_mat_Flag(matstate) == false)
return;
/* ----------------
* 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);
/* ----------------
* restore the scan to the previously marked position
* ----------------
*/
sdesc = get_css_currentScanDesc((CommonScanState) matstate);
heap_restrpos(sdesc);
}
#endif
#endif

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