mirror of
https://github.com/postgres/postgres.git
synced 2025-08-08 06:02:22 +03:00
pgindent run.
This commit is contained in:
@@ -299,11 +299,12 @@ gts_compress(PG_FUNCTION_ARGS)
|
||||
if (entry->leafkey)
|
||||
{
|
||||
TSKEY *r = (TSKEY *) palloc(sizeof(TSKEY));
|
||||
|
||||
retval = palloc(sizeof(GISTENTRY));
|
||||
r->lower = r->upper = *(Timestamp *) (entry->key);
|
||||
gistentryinit(*retval, PointerGetDatum(r),
|
||||
entry->rel, entry->page,
|
||||
entry->offset, sizeof(TSKEY), FALSE);
|
||||
entry->rel, entry->page,
|
||||
entry->offset, sizeof(TSKEY), FALSE);
|
||||
}
|
||||
else
|
||||
retval = entry;
|
||||
@@ -398,8 +399,8 @@ gts_penalty(PG_FUNCTION_ARGS)
|
||||
|
||||
intr = DatumGetIntervalP(DirectFunctionCall2(
|
||||
timestamp_mi,
|
||||
TimestampGetDatumFast(newentry->upper),
|
||||
TimestampGetDatumFast(origentry->upper)));
|
||||
TimestampGetDatumFast(newentry->upper),
|
||||
TimestampGetDatumFast(origentry->upper)));
|
||||
|
||||
/* see interval_larger */
|
||||
*result = Max(intr->time + intr->month * (30.0 * 86400), 0);
|
||||
@@ -407,8 +408,8 @@ gts_penalty(PG_FUNCTION_ARGS)
|
||||
|
||||
intr = DatumGetIntervalP(DirectFunctionCall2(
|
||||
timestamp_mi,
|
||||
TimestampGetDatumFast(origentry->lower),
|
||||
TimestampGetDatumFast(newentry->lower)));
|
||||
TimestampGetDatumFast(origentry->lower),
|
||||
TimestampGetDatumFast(newentry->lower)));
|
||||
|
||||
/* see interval_larger */
|
||||
*result += Max(intr->time + intr->month * (30.0 * 86400), 0);
|
||||
@@ -473,8 +474,8 @@ tskey_cmp(const void *a, const void *b)
|
||||
return DatumGetInt32(
|
||||
DirectFunctionCall2(
|
||||
timestamp_cmp,
|
||||
TimestampGetDatumFast(((TSKEY *) (((RIX *) a)->r))->lower),
|
||||
TimestampGetDatumFast(((TSKEY *) (((RIX *) b)->r))->lower)
|
||||
TimestampGetDatumFast(((TSKEY *) (((RIX *) a)->r))->lower),
|
||||
TimestampGetDatumFast(((TSKEY *) (((RIX *) b)->r))->lower)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@@ -73,11 +73,11 @@ bool cube_right(NDBOX * a, NDBOX * b);
|
||||
bool cube_lt(NDBOX * a, NDBOX * b);
|
||||
bool cube_gt(NDBOX * a, NDBOX * b);
|
||||
double *cube_distance(NDBOX * a, NDBOX * b);
|
||||
int cube_dim(NDBOX *a);
|
||||
double *cube_ll_coord(NDBOX * a, int n);
|
||||
double *cube_ur_coord(NDBOX * a, int n);
|
||||
bool cube_is_point(NDBOX * a);
|
||||
NDBOX *cube_enlarge(NDBOX * a, double * r, int n);
|
||||
int cube_dim(NDBOX * a);
|
||||
double *cube_ll_coord(NDBOX * a, int n);
|
||||
double *cube_ur_coord(NDBOX * a, int n);
|
||||
bool cube_is_point(NDBOX * a);
|
||||
NDBOX *cube_enlarge(NDBOX * a, double *r, int n);
|
||||
|
||||
|
||||
/*
|
||||
@@ -111,11 +111,11 @@ NDBOX *
|
||||
cube(text *str)
|
||||
{
|
||||
return cube_in(DatumGetCString(DirectFunctionCall1(textout,
|
||||
PointerGetDatum(str))));
|
||||
PointerGetDatum(str))));
|
||||
}
|
||||
|
||||
char *
|
||||
cube_out(NDBOX *cube)
|
||||
cube_out(NDBOX * cube)
|
||||
{
|
||||
StringInfoData buf;
|
||||
bool equal = true;
|
||||
@@ -542,14 +542,14 @@ cube_union(NDBOX * a, NDBOX * b)
|
||||
if (a->dim >= b->dim)
|
||||
{
|
||||
result = palloc(a->size);
|
||||
memset(result, 0, a->size);
|
||||
memset(result, 0, a->size);
|
||||
result->size = a->size;
|
||||
result->dim = a->dim;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = palloc(b->size);
|
||||
memset(result, 0, b->size);
|
||||
memset(result, 0, b->size);
|
||||
result->size = b->size;
|
||||
result->dim = b->dim;
|
||||
}
|
||||
@@ -580,12 +580,12 @@ cube_union(NDBOX * a, NDBOX * b)
|
||||
|
||||
/* compute the union */
|
||||
for (i = 0; i < a->dim; i++)
|
||||
{
|
||||
{
|
||||
result->x[i] =
|
||||
min(min(a->x[i], a->x[i + a->dim]), result->x[i]);
|
||||
min(min(a->x[i], a->x[i + a->dim]), result->x[i]);
|
||||
result->x[i + a->dim] = max(max(a->x[i],
|
||||
a->x[i + a->dim]), result->x[i + a->dim]);
|
||||
}
|
||||
a->x[i + a->dim]), result->x[i + a->dim]);
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
@@ -600,14 +600,14 @@ cube_inter(NDBOX * a, NDBOX * b)
|
||||
if (a->dim >= b->dim)
|
||||
{
|
||||
result = palloc(a->size);
|
||||
memset(result, 0, a->size);
|
||||
memset(result, 0, a->size);
|
||||
result->size = a->size;
|
||||
result->dim = a->dim;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = palloc(b->size);
|
||||
memset(result, 0, b->size);
|
||||
memset(result, 0, b->size);
|
||||
result->size = b->size;
|
||||
result->dim = b->dim;
|
||||
}
|
||||
@@ -638,12 +638,12 @@ cube_inter(NDBOX * a, NDBOX * b)
|
||||
|
||||
/* compute the intersection */
|
||||
for (i = 0; i < a->dim; i++)
|
||||
{
|
||||
{
|
||||
result->x[i] =
|
||||
max(min(a->x[i], a->x[i + a->dim]), result->x[i]);
|
||||
max(min(a->x[i], a->x[i + a->dim]), result->x[i]);
|
||||
result->x[i + a->dim] = min(max(a->x[i],
|
||||
a->x[i + a->dim]), result->x[i + a->dim]);
|
||||
}
|
||||
a->x[i + a->dim]), result->x[i + a->dim]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Is it OK to return a non-null intersection for non-overlapping
|
||||
@@ -700,8 +700,8 @@ cube_over_left(NDBOX * a, NDBOX * b)
|
||||
return (FALSE);
|
||||
|
||||
return (min(a->x[a->dim - 1], a->x[2 * a->dim - 1]) <=
|
||||
min(b->x[b->dim - 1], b->x[2 * b->dim - 1]) &&
|
||||
!cube_left(a, b) && !cube_right(a, b));
|
||||
min(b->x[b->dim - 1], b->x[2 * b->dim - 1]) &&
|
||||
!cube_left(a, b) && !cube_right(a, b));
|
||||
}
|
||||
|
||||
/* is the left edge of (a) located to the right of
|
||||
@@ -713,8 +713,8 @@ cube_over_right(NDBOX * a, NDBOX * b)
|
||||
return (FALSE);
|
||||
|
||||
return (min(a->x[a->dim - 1], a->x[2 * a->dim - 1]) >=
|
||||
min(b->x[b->dim - 1], b->x[2 * b->dim - 1]) &&
|
||||
!cube_left(a, b) && !cube_right(a, b));
|
||||
min(b->x[b->dim - 1], b->x[2 * b->dim - 1]) &&
|
||||
!cube_left(a, b) && !cube_right(a, b));
|
||||
}
|
||||
|
||||
|
||||
@@ -727,7 +727,7 @@ cube_left(NDBOX * a, NDBOX * b)
|
||||
return (FALSE);
|
||||
|
||||
return (min(a->x[a->dim - 1], a->x[2 * a->dim - 1]) <
|
||||
min(b->x[0], b->x[b->dim]));
|
||||
min(b->x[0], b->x[b->dim]));
|
||||
}
|
||||
|
||||
/* return 'true' if the projection of 'a' is
|
||||
@@ -739,7 +739,7 @@ cube_right(NDBOX * a, NDBOX * b)
|
||||
return (FALSE);
|
||||
|
||||
return (min(a->x[0], a->x[a->dim]) >
|
||||
min(b->x[b->dim - 1], b->x[2 * b->dim - 1]));
|
||||
min(b->x[b->dim - 1], b->x[2 * b->dim - 1]));
|
||||
}
|
||||
|
||||
/* make up a metric in which one box will be 'lower' than the other
|
||||
@@ -759,19 +759,19 @@ cube_lt(NDBOX * a, NDBOX * b)
|
||||
for (i = 0; i < dim; i++)
|
||||
{
|
||||
if (min(a->x[i], a->x[a->dim + i]) >
|
||||
min(b->x[i], b->x[b->dim + i]))
|
||||
min(b->x[i], b->x[b->dim + i]))
|
||||
return (FALSE);
|
||||
if (min(a->x[i], a->x[a->dim + i]) <
|
||||
min(b->x[i], b->x[b->dim + i]))
|
||||
min(b->x[i], b->x[b->dim + i]))
|
||||
return (TRUE);
|
||||
}
|
||||
for (i = 0; i < dim; i++)
|
||||
{
|
||||
if (max(a->x[i], a->x[a->dim + i]) >
|
||||
max(b->x[i], b->x[b->dim + i]))
|
||||
max(b->x[i], b->x[b->dim + i]))
|
||||
return (FALSE);
|
||||
if (max(a->x[i], a->x[a->dim + i]) <
|
||||
max(b->x[i], b->x[b->dim + i]))
|
||||
max(b->x[i], b->x[b->dim + i]))
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
@@ -792,11 +792,12 @@ cube_lt(NDBOX * a, NDBOX * b)
|
||||
if (max(a->x[i], a->x[a->dim + i]) < 0)
|
||||
return (TRUE);
|
||||
}
|
||||
/*
|
||||
* if all common dimensions are equal, the cube with more
|
||||
* dimensions wins
|
||||
*/
|
||||
return (FALSE);
|
||||
|
||||
/*
|
||||
* if all common dimensions are equal, the cube with more
|
||||
* dimensions wins
|
||||
*/
|
||||
return (FALSE);
|
||||
}
|
||||
if (a->dim < b->dim)
|
||||
{
|
||||
@@ -814,11 +815,12 @@ cube_lt(NDBOX * a, NDBOX * b)
|
||||
if (max(b->x[i], b->x[b->dim + i]) < 0)
|
||||
return (FALSE);
|
||||
}
|
||||
/*
|
||||
* if all common dimensions are equal, the cube with more
|
||||
* dimensions wins
|
||||
*/
|
||||
return (TRUE);
|
||||
|
||||
/*
|
||||
* if all common dimensions are equal, the cube with more
|
||||
* dimensions wins
|
||||
*/
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
return (FALSE);
|
||||
@@ -840,19 +842,19 @@ cube_gt(NDBOX * a, NDBOX * b)
|
||||
for (i = 0; i < dim; i++)
|
||||
{
|
||||
if (min(a->x[i], a->x[a->dim + i]) <
|
||||
min(b->x[i], b->x[b->dim + i]))
|
||||
min(b->x[i], b->x[b->dim + i]))
|
||||
return (FALSE);
|
||||
if (min(a->x[i], a->x[a->dim + i]) >
|
||||
min(b->x[i], b->x[b->dim + i]))
|
||||
min(b->x[i], b->x[b->dim + i]))
|
||||
return (TRUE);
|
||||
}
|
||||
for (i = 0; i < dim; i++)
|
||||
{
|
||||
if (max(a->x[i], a->x[a->dim + i]) <
|
||||
max(b->x[i], b->x[b->dim + i]))
|
||||
max(b->x[i], b->x[b->dim + i]))
|
||||
return (FALSE);
|
||||
if (max(a->x[i], a->x[a->dim + i]) >
|
||||
max(b->x[i], b->x[b->dim + i]))
|
||||
max(b->x[i], b->x[b->dim + i]))
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
@@ -874,11 +876,12 @@ cube_gt(NDBOX * a, NDBOX * b)
|
||||
if (max(a->x[i], a->x[a->dim + i]) > 0)
|
||||
return (TRUE);
|
||||
}
|
||||
/*
|
||||
* if all common dimensions are equal, the cube with more
|
||||
* dimensions wins
|
||||
*/
|
||||
return (TRUE);
|
||||
|
||||
/*
|
||||
* if all common dimensions are equal, the cube with more
|
||||
* dimensions wins
|
||||
*/
|
||||
return (TRUE);
|
||||
}
|
||||
if (a->dim < b->dim)
|
||||
{
|
||||
@@ -896,11 +899,12 @@ cube_gt(NDBOX * a, NDBOX * b)
|
||||
if (max(b->x[i], b->x[b->dim + i]) > 0)
|
||||
return (FALSE);
|
||||
}
|
||||
/*
|
||||
* if all common dimensions are equal, the cube with more
|
||||
* dimensions wins
|
||||
*/
|
||||
return (FALSE);
|
||||
|
||||
/*
|
||||
* if all common dimensions are equal, the cube with more
|
||||
* dimensions wins
|
||||
*/
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
return (FALSE);
|
||||
@@ -928,18 +932,18 @@ cube_same(NDBOX * a, NDBOX * b)
|
||||
for (i = 0; i < b->dim; i++)
|
||||
{
|
||||
if (min(a->x[i], a->x[a->dim + i]) !=
|
||||
min(b->x[i], b->x[b->dim + i]))
|
||||
min(b->x[i], b->x[b->dim + i]))
|
||||
return (FALSE);
|
||||
if (max(a->x[i], a->x[a->dim + i]) !=
|
||||
max(b->x[i], b->x[b->dim + i]))
|
||||
max(b->x[i], b->x[b->dim + i]))
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
/*
|
||||
* all dimensions of (b) are compared to those of (a); instead of
|
||||
* those in (a) absent in (b), compare (a) to zero
|
||||
* Since both LL and UR coordinates are compared to zero, we can
|
||||
* just check them all without worrying about which is which.
|
||||
* those in (a) absent in (b), compare (a) to zero Since both LL and
|
||||
* UR coordinates are compared to zero, we can just check them all
|
||||
* without worrying about which is which.
|
||||
*/
|
||||
for (i = b->dim; i < a->dim; i++)
|
||||
{
|
||||
@@ -974,9 +978,9 @@ cube_contains(NDBOX * a, NDBOX * b)
|
||||
{
|
||||
/*
|
||||
* the further comparisons will make sense if the excess
|
||||
* dimensions of (b) were zeroes
|
||||
* Since both UL and UR coordinates must be zero, we can
|
||||
* check them all without worrying about which is which.
|
||||
* dimensions of (b) were zeroes Since both UL and UR coordinates
|
||||
* must be zero, we can check them all without worrying about
|
||||
* which is which.
|
||||
*/
|
||||
for (i = a->dim; i < b->dim; i++)
|
||||
{
|
||||
@@ -991,10 +995,10 @@ cube_contains(NDBOX * a, NDBOX * b)
|
||||
for (i = 0; i < min(a->dim, b->dim); i++)
|
||||
{
|
||||
if (min(a->x[i], a->x[a->dim + i]) >
|
||||
min(b->x[i], b->x[b->dim + i]))
|
||||
min(b->x[i], b->x[b->dim + i]))
|
||||
return (FALSE);
|
||||
if (max(a->x[i], a->x[a->dim + i]) <
|
||||
max(b->x[i], b->x[b->dim + i]))
|
||||
max(b->x[i], b->x[b->dim + i]))
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
@@ -1039,10 +1043,10 @@ cube_overlap(NDBOX * a, NDBOX * b)
|
||||
for (i = 0; i < b->dim; i++)
|
||||
{
|
||||
if (min(a->x[i], a->x[a->dim + i]) >
|
||||
max(b->x[i], b->x[b->dim + i]))
|
||||
max(b->x[i], b->x[b->dim + i]))
|
||||
return (FALSE);
|
||||
if (max(a->x[i], a->x[a->dim + i]) <
|
||||
min(b->x[i], b->x[b->dim + i]))
|
||||
min(b->x[i], b->x[b->dim + i]))
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
@@ -1122,87 +1126,94 @@ distance_1D(double a1, double a2, double b1, double b2)
|
||||
bool
|
||||
cube_is_point(NDBOX * a)
|
||||
{
|
||||
int i,
|
||||
j;
|
||||
for (i = 0, j = a->dim; i < a->dim; i++, j++)
|
||||
{
|
||||
if (a->x[i] != a->x[j]) return FALSE;
|
||||
}
|
||||
int i,
|
||||
j;
|
||||
|
||||
return TRUE;
|
||||
for (i = 0, j = a->dim; i < a->dim; i++, j++)
|
||||
{
|
||||
if (a->x[i] != a->x[j])
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Return dimensions in use in the data structure */
|
||||
int
|
||||
cube_dim(NDBOX * a)
|
||||
{
|
||||
/* Other things will break before unsigned int doesn't fit. */
|
||||
return a->dim;
|
||||
/* Other things will break before unsigned int doesn't fit. */
|
||||
return a->dim;
|
||||
}
|
||||
|
||||
/* Return a specific normalized LL coordinate */
|
||||
double *
|
||||
cube_ll_coord(NDBOX * a, int n)
|
||||
{
|
||||
double *result;
|
||||
double *result;
|
||||
|
||||
result = (double *) palloc(sizeof(double));
|
||||
*result = 0;
|
||||
if (a->dim >= n && n > 0)
|
||||
*result = min(a->x[n-1], a->x[a->dim + n-1]);
|
||||
return result;
|
||||
*result = 0;
|
||||
if (a->dim >= n && n > 0)
|
||||
*result = min(a->x[n - 1], a->x[a->dim + n - 1]);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Return a specific normalized UR coordinate */
|
||||
double *
|
||||
cube_ur_coord(NDBOX * a, int n)
|
||||
{
|
||||
double *result;
|
||||
double *result;
|
||||
|
||||
result = (double *) palloc(sizeof(double));
|
||||
*result = 0;
|
||||
if (a->dim >= n && n > 0)
|
||||
*result = max(a->x[n-1], a->x[a->dim + n-1]);
|
||||
return result;
|
||||
*result = 0;
|
||||
if (a->dim >= n && n > 0)
|
||||
*result = max(a->x[n - 1], a->x[a->dim + n - 1]);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Increase or decrease box size by a radius in at least n dimensions. */
|
||||
NDBOX *
|
||||
cube_enlarge(NDBOX * a, double * r, int n)
|
||||
cube_enlarge(NDBOX * a, double *r, int n)
|
||||
{
|
||||
NDBOX *result;
|
||||
int dim = 0;
|
||||
int size;
|
||||
int i,
|
||||
j;
|
||||
if (*r > 0 && n > 0) dim = n;
|
||||
if (a->dim > dim) dim = a->dim;
|
||||
size = offsetof(NDBOX, x[0]) + sizeof(double) * dim * 2;
|
||||
result = (NDBOX *) palloc(size);
|
||||
memset(result, 0, size);
|
||||
result->size = size;
|
||||
result->dim = dim;
|
||||
NDBOX *result;
|
||||
int dim = 0;
|
||||
int size;
|
||||
int i,
|
||||
j;
|
||||
|
||||
if (*r > 0 && n > 0)
|
||||
dim = n;
|
||||
if (a->dim > dim)
|
||||
dim = a->dim;
|
||||
size = offsetof(NDBOX, x[0]) + sizeof(double) * dim * 2;
|
||||
result = (NDBOX *) palloc(size);
|
||||
memset(result, 0, size);
|
||||
result->size = size;
|
||||
result->dim = dim;
|
||||
for (i = 0, j = dim; i < a->dim; i++, j++)
|
||||
{
|
||||
if (a->x[i] >= a->x[j])
|
||||
{
|
||||
result->x[i] = a->x[j] - *r;
|
||||
result->x[j] = a->x[i] + *r;
|
||||
}
|
||||
else
|
||||
{
|
||||
result->x[i] = a->x[i] - *r;
|
||||
result->x[j] = a->x[j] + *r;
|
||||
}
|
||||
if (result->x[i] > result->x[j])
|
||||
{
|
||||
result->x[i] = (result->x[i] + result->x[j]) / 2;
|
||||
result->x[j] = result->x[i];
|
||||
}
|
||||
}
|
||||
/* dim > a->dim only if r > 0 */
|
||||
{
|
||||
if (a->x[i] >= a->x[j])
|
||||
{
|
||||
result->x[i] = a->x[j] - *r;
|
||||
result->x[j] = a->x[i] + *r;
|
||||
}
|
||||
else
|
||||
{
|
||||
result->x[i] = a->x[i] - *r;
|
||||
result->x[j] = a->x[j] + *r;
|
||||
}
|
||||
if (result->x[i] > result->x[j])
|
||||
{
|
||||
result->x[i] = (result->x[i] + result->x[j]) / 2;
|
||||
result->x[j] = result->x[i];
|
||||
}
|
||||
}
|
||||
/* dim > a->dim only if r > 0 */
|
||||
for (; i < dim; i++, j++)
|
||||
{
|
||||
result->x[i] = -*r;
|
||||
result->x[j] = *r;
|
||||
}
|
||||
return result;
|
||||
{
|
||||
result->x[i] = -*r;
|
||||
result->x[j] = *r;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@@ -309,7 +309,7 @@ do_create(PGconn *conn, char *table, dbhead * dbh)
|
||||
{
|
||||
strcat(query, " varchar");
|
||||
snprintf(t, 20, "(%d)",
|
||||
dbh->db_fields[i].db_flen);
|
||||
dbh->db_fields[i].db_flen);
|
||||
strcat(query, t);
|
||||
}
|
||||
else
|
||||
@@ -468,8 +468,8 @@ do_inserts(PGconn *conn, char *table, dbhead * dbh)
|
||||
if ((strlen(foo) == 8) && isinteger(foo))
|
||||
{
|
||||
snprintf(pgdate, 11, "%c%c%c%c-%c%c-%c%c",
|
||||
foo[0], foo[1], foo[2], foo[3],
|
||||
foo[4], foo[5], foo[6], foo[7]);
|
||||
foo[0], foo[1], foo[2], foo[3],
|
||||
foo[4], foo[5], foo[6], foo[7]);
|
||||
strcat(query, pgdate);
|
||||
}
|
||||
else
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -74,6 +74,6 @@ extern Datum dblink_build_sql_delete(PG_FUNCTION_ARGS);
|
||||
extern Datum dblink_build_sql_update(PG_FUNCTION_ARGS);
|
||||
extern Datum dblink_current_query(PG_FUNCTION_ARGS);
|
||||
|
||||
extern char *debug_query_string;
|
||||
extern char *debug_query_string;
|
||||
|
||||
#endif /* DBLINK_H */
|
||||
|
@@ -1,20 +1,20 @@
|
||||
/****************************************************************************
|
||||
* pending.c
|
||||
* $Id: pending.c,v 1.1 2002/06/23 21:58:08 momjian Exp $
|
||||
* $Id: pending.c,v 1.2 2002/09/04 20:31:06 momjian Exp $
|
||||
*
|
||||
* This file contains a trigger for Postgresql-7.x to record changes to tables
|
||||
* to a pending table for mirroring.
|
||||
* All tables that should be mirrored should have this trigger hooked up to it.
|
||||
*
|
||||
* Written by Steven Singer (ssinger@navtechinc.com)
|
||||
* (c) 2001-2002 Navtech Systems Support Inc.
|
||||
* Released under the GNU Public License version 2. See COPYING.
|
||||
* Written by Steven Singer (ssinger@navtechinc.com)
|
||||
* (c) 2001-2002 Navtech Systems Support Inc.
|
||||
* Released under the GNU Public License version 2. See COPYING.
|
||||
*
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*
|
||||
***************************************************************************/
|
||||
@@ -22,29 +22,33 @@
|
||||
#include <commands/trigger.h>
|
||||
#include <postgres.h>
|
||||
|
||||
enum FieldUsage {PRIMARY=0,NONPRIMARY,ALL,NUM_FIELDUSAGE};
|
||||
enum FieldUsage
|
||||
{
|
||||
PRIMARY = 0, NONPRIMARY, ALL, NUM_FIELDUSAGE
|
||||
};
|
||||
|
||||
int storePending(char * cpTableName, HeapTuple tBeforeTuple,
|
||||
HeapTuple tAfterTuple,
|
||||
TupleDesc tTupdesc,
|
||||
TriggerData * tpTrigdata,char cOp);
|
||||
int storePending(char *cpTableName, HeapTuple tBeforeTuple,
|
||||
HeapTuple tAfterTuple,
|
||||
TupleDesc tTupdesc,
|
||||
TriggerData *tpTrigdata, char cOp);
|
||||
|
||||
int storeKeyInfo(char * cpTableName, HeapTuple tTupleData, TupleDesc tTuplDesc,
|
||||
TriggerData * tpTrigdata);
|
||||
int storeData(char * cpTableName,HeapTuple tTupleData,TupleDesc tTupleDesc,
|
||||
TriggerData * tpTrigData,int iIncludeKeyData);
|
||||
|
||||
int2vector * getPrimaryKey(Oid tblOid);
|
||||
int storeKeyInfo(char *cpTableName, HeapTuple tTupleData, TupleDesc tTuplDesc,
|
||||
TriggerData *tpTrigdata);
|
||||
int storeData(char *cpTableName, HeapTuple tTupleData, TupleDesc tTupleDesc,
|
||||
TriggerData *tpTrigData, int iIncludeKeyData);
|
||||
|
||||
char * packageData(HeapTuple tTupleData, TupleDesc tTupleDecs,
|
||||
TriggerData * tTrigData,
|
||||
enum FieldUsage eKeyUsage );
|
||||
int2vector *getPrimaryKey(Oid tblOid);
|
||||
|
||||
char *packageData(HeapTuple tTupleData, TupleDesc tTupleDecs,
|
||||
TriggerData *tTrigData,
|
||||
enum FieldUsage eKeyUsage);
|
||||
|
||||
#define BUFFER_SIZE 256
|
||||
#define MAX_OID_LEN 10
|
||||
|
||||
|
||||
extern Datum recordchange(PG_FUNCTION_ARGS);
|
||||
|
||||
PG_FUNCTION_INFO_V1(recordchange);
|
||||
|
||||
|
||||
@@ -54,394 +58,430 @@ PG_FUNCTION_INFO_V1(recordchange);
|
||||
* table the trigger was applied to. If this name is incorrect so will the
|
||||
* mirroring.
|
||||
****************************************************************************/
|
||||
Datum recordchange(PG_FUNCTION_ARGS) {
|
||||
TriggerData * trigdata;
|
||||
TupleDesc tupdesc;
|
||||
HeapTuple beforeTuple=NULL;
|
||||
HeapTuple afterTuple=NULL;
|
||||
HeapTuple retTuple=NULL;
|
||||
char * tblname;
|
||||
char op;
|
||||
if(fcinfo->context!=NULL) {
|
||||
|
||||
if(SPI_connect() < 0) {
|
||||
elog(NOTICE,"storePending could not connect to SPI");
|
||||
return -1;
|
||||
}
|
||||
trigdata = (TriggerData*)fcinfo->context;
|
||||
/* Extract the table name */
|
||||
tblname = SPI_getrelname(trigdata->tg_relation);
|
||||
tupdesc = trigdata->tg_relation->rd_att;
|
||||
if(TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) {
|
||||
retTuple = trigdata->tg_newtuple;
|
||||
beforeTuple = trigdata->tg_trigtuple;
|
||||
afterTuple = trigdata->tg_newtuple;
|
||||
op='u';
|
||||
Datum
|
||||
recordchange(PG_FUNCTION_ARGS)
|
||||
{
|
||||
TriggerData *trigdata;
|
||||
TupleDesc tupdesc;
|
||||
HeapTuple beforeTuple = NULL;
|
||||
HeapTuple afterTuple = NULL;
|
||||
HeapTuple retTuple = NULL;
|
||||
char *tblname;
|
||||
char op;
|
||||
|
||||
}
|
||||
else if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event)) {
|
||||
retTuple = trigdata->tg_trigtuple;
|
||||
afterTuple = trigdata->tg_trigtuple;
|
||||
op = 'i';
|
||||
}
|
||||
else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)) {
|
||||
retTuple = trigdata->tg_trigtuple;
|
||||
beforeTuple = trigdata->tg_trigtuple;
|
||||
op = 'd';
|
||||
}
|
||||
if (fcinfo->context != NULL)
|
||||
{
|
||||
|
||||
if(storePending(tblname,beforeTuple,afterTuple,tupdesc,trigdata,op)) {
|
||||
/* An error occoured. Skip the operation. */
|
||||
elog(ERROR,"Operation could not be mirrored");
|
||||
return PointerGetDatum(NULL);
|
||||
|
||||
}
|
||||
if (SPI_connect() < 0)
|
||||
{
|
||||
elog(NOTICE, "storePending could not connect to SPI");
|
||||
return -1;
|
||||
}
|
||||
trigdata = (TriggerData *) fcinfo->context;
|
||||
/* Extract the table name */
|
||||
tblname = SPI_getrelname(trigdata->tg_relation);
|
||||
tupdesc = trigdata->tg_relation->rd_att;
|
||||
if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
|
||||
{
|
||||
retTuple = trigdata->tg_newtuple;
|
||||
beforeTuple = trigdata->tg_trigtuple;
|
||||
afterTuple = trigdata->tg_newtuple;
|
||||
op = 'u';
|
||||
|
||||
}
|
||||
else if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
|
||||
{
|
||||
retTuple = trigdata->tg_trigtuple;
|
||||
afterTuple = trigdata->tg_trigtuple;
|
||||
op = 'i';
|
||||
}
|
||||
else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
|
||||
{
|
||||
retTuple = trigdata->tg_trigtuple;
|
||||
beforeTuple = trigdata->tg_trigtuple;
|
||||
op = 'd';
|
||||
}
|
||||
|
||||
if (storePending(tblname, beforeTuple, afterTuple, tupdesc, trigdata, op))
|
||||
{
|
||||
/* An error occoured. Skip the operation. */
|
||||
elog(ERROR, "Operation could not be mirrored");
|
||||
return PointerGetDatum(NULL);
|
||||
|
||||
}
|
||||
#if defined DEBUG_OUTPUT
|
||||
elog(NOTICE,"Returning on success");
|
||||
elog(NOTICE, "Returning on success");
|
||||
#endif
|
||||
SPI_finish();
|
||||
return PointerGetDatum(retTuple);
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* Not being called as a trigger.
|
||||
*/
|
||||
return PointerGetDatum(NULL);
|
||||
}
|
||||
SPI_finish();
|
||||
return PointerGetDatum(retTuple);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Not being called as a trigger.
|
||||
*/
|
||||
return PointerGetDatum(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* Constructs and executes an SQL query to write a record of this tuple change
|
||||
* to the pending table.
|
||||
* to the pending table.
|
||||
*****************************************************************************/
|
||||
int storePending(char * cpTableName, HeapTuple tBeforeTuple,
|
||||
HeapTuple tAfterTuple,
|
||||
TupleDesc tTupDesc,
|
||||
TriggerData * tpTrigData,char cOp) {
|
||||
char * cpQueryBase = "INSERT INTO \"Pending\" (\"TableName\",\"Op\",\"XID\") VALUES ($1,$2,$3)";
|
||||
int
|
||||
storePending(char *cpTableName, HeapTuple tBeforeTuple,
|
||||
HeapTuple tAfterTuple,
|
||||
TupleDesc tTupDesc,
|
||||
TriggerData *tpTrigData, char cOp)
|
||||
{
|
||||
char *cpQueryBase = "INSERT INTO \"Pending\" (\"TableName\",\"Op\",\"XID\") VALUES ($1,$2,$3)";
|
||||
|
||||
int iResult=0;
|
||||
HeapTuple tCurTuple; // Points the current tuple(before or after)
|
||||
Datum saPlanData[4];
|
||||
Oid taPlanArgTypes[3] = {NAMEOID,CHAROID,INT4OID};
|
||||
void * vpPlan;
|
||||
int iResult = 0;
|
||||
HeapTuple tCurTuple;
|
||||
|
||||
tCurTuple = tBeforeTuple ? tBeforeTuple : tAfterTuple;
|
||||
//Points the current tuple(before or after)
|
||||
Datum saPlanData[4];
|
||||
Oid taPlanArgTypes[3] = {NAMEOID, CHAROID, INT4OID};
|
||||
void *vpPlan;
|
||||
|
||||
|
||||
tCurTuple = tBeforeTuple ? tBeforeTuple : tAfterTuple;
|
||||
|
||||
|
||||
vpPlan = SPI_prepare(cpQueryBase,3,taPlanArgTypes);
|
||||
if(vpPlan==NULL) {
|
||||
elog(NOTICE,"Error creating plan");
|
||||
}
|
||||
// SPI_saveplan(vpPlan);
|
||||
|
||||
saPlanData[0] = PointerGetDatum(cpTableName);
|
||||
saPlanData[1] = CharGetDatum(cOp);
|
||||
saPlanData[2] = Int32GetDatum(GetCurrentTransactionId());
|
||||
|
||||
|
||||
iResult = SPI_execp(vpPlan,saPlanData,NULL,1);
|
||||
if(iResult < 0) {
|
||||
elog(NOTICE,"storedPending fired (%s) returned %d",cpQueryBase,iResult);
|
||||
}
|
||||
|
||||
|
||||
|
||||
vpPlan = SPI_prepare(cpQueryBase, 3, taPlanArgTypes);
|
||||
if (vpPlan == NULL)
|
||||
elog(NOTICE, "Error creating plan");
|
||||
/* SPI_saveplan(vpPlan); */
|
||||
|
||||
saPlanData[0] = PointerGetDatum(cpTableName);
|
||||
saPlanData[1] = CharGetDatum(cOp);
|
||||
saPlanData[2] = Int32GetDatum(GetCurrentTransactionId());
|
||||
|
||||
|
||||
iResult = SPI_execp(vpPlan, saPlanData, NULL, 1);
|
||||
if (iResult < 0)
|
||||
elog(NOTICE, "storedPending fired (%s) returned %d", cpQueryBase, iResult);
|
||||
|
||||
|
||||
#if defined DEBUG_OUTPUT
|
||||
elog(NOTICE,"row successfully stored in pending table");
|
||||
elog(NOTICE, "row successfully stored in pending table");
|
||||
#endif
|
||||
|
||||
if(cOp=='d') {
|
||||
/**
|
||||
* This is a record of a delete operation.
|
||||
* Just store the key data.
|
||||
*/
|
||||
iResult = storeKeyInfo(cpTableName,tBeforeTuple,tTupDesc,tpTrigData);
|
||||
}
|
||||
else if (cOp=='i') {
|
||||
/**
|
||||
* An Insert operation.
|
||||
* Store all data
|
||||
*/
|
||||
iResult = storeData(cpTableName,tAfterTuple,tTupDesc,tpTrigData,TRUE);
|
||||
if (cOp == 'd')
|
||||
{
|
||||
/**
|
||||
* This is a record of a delete operation.
|
||||
* Just store the key data.
|
||||
*/
|
||||
iResult = storeKeyInfo(cpTableName, tBeforeTuple, tTupDesc, tpTrigData);
|
||||
}
|
||||
else if (cOp == 'i')
|
||||
{
|
||||
/**
|
||||
* An Insert operation.
|
||||
* Store all data
|
||||
*/
|
||||
iResult = storeData(cpTableName, tAfterTuple, tTupDesc, tpTrigData, TRUE);
|
||||
|
||||
}
|
||||
else {
|
||||
/* op must be an update. */
|
||||
iResult = storeKeyInfo(cpTableName,tBeforeTuple,tTupDesc,tpTrigData);
|
||||
iResult = iResult ? iResult : storeData(cpTableName,tAfterTuple,tTupDesc,
|
||||
tpTrigData,TRUE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* op must be an update. */
|
||||
iResult = storeKeyInfo(cpTableName, tBeforeTuple, tTupDesc, tpTrigData);
|
||||
iResult = iResult ? iResult : storeData(cpTableName, tAfterTuple, tTupDesc,
|
||||
tpTrigData, TRUE);
|
||||
}
|
||||
|
||||
#if defined DEBUG_OUTPUT
|
||||
elog(NOTICE,"DOne storing keyinfo");
|
||||
elog(NOTICE, "DOne storing keyinfo");
|
||||
#endif
|
||||
|
||||
return iResult;
|
||||
|
||||
return iResult;
|
||||
|
||||
}
|
||||
|
||||
int storeKeyInfo(char * cpTableName, HeapTuple tTupleData,
|
||||
TupleDesc tTupleDesc,
|
||||
TriggerData * tpTrigData) {
|
||||
|
||||
Oid saPlanArgTypes[1] = {NAMEOID};
|
||||
char * insQuery = "INSERT INTO \"PendingData\" (\"SeqId\",\"IsKey\",\"Data\") VALUES(currval('\"Pending_SeqId_seq\"'),'t',$1)";
|
||||
void * pplan;
|
||||
Datum saPlanData[1];
|
||||
char * cpKeyData;
|
||||
int iRetCode;
|
||||
int
|
||||
storeKeyInfo(char *cpTableName, HeapTuple tTupleData,
|
||||
TupleDesc tTupleDesc,
|
||||
TriggerData *tpTrigData)
|
||||
{
|
||||
|
||||
pplan = SPI_prepare(insQuery,1,saPlanArgTypes);
|
||||
if(pplan==NULL) {
|
||||
elog(NOTICE,"Could not prepare INSERT plan");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// pplan = SPI_saveplan(pplan);
|
||||
cpKeyData = packageData(tTupleData, tTupleDesc,tpTrigData,PRIMARY);
|
||||
Oid saPlanArgTypes[1] = {NAMEOID};
|
||||
char *insQuery = "INSERT INTO \"PendingData\" (\"SeqId\",\"IsKey\",\"Data\") VALUES(currval('\"Pending_SeqId_seq\"'),'t',$1)";
|
||||
void *pplan;
|
||||
Datum saPlanData[1];
|
||||
char *cpKeyData;
|
||||
int iRetCode;
|
||||
|
||||
pplan = SPI_prepare(insQuery, 1, saPlanArgTypes);
|
||||
if (pplan == NULL)
|
||||
{
|
||||
elog(NOTICE, "Could not prepare INSERT plan");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* pplan = SPI_saveplan(pplan); */
|
||||
cpKeyData = packageData(tTupleData, tTupleDesc, tpTrigData, PRIMARY);
|
||||
#if defined DEBUG_OUTPUT
|
||||
elog(NOTICE,cpKeyData);
|
||||
elog(NOTICE, cpKeyData);
|
||||
#endif
|
||||
saPlanData[0] = PointerGetDatum(cpKeyData);
|
||||
|
||||
iRetCode = SPI_execp(pplan,saPlanData,NULL,1);
|
||||
saPlanData[0] = PointerGetDatum(cpKeyData);
|
||||
|
||||
if(cpKeyData!=NULL) {
|
||||
SPI_pfree(cpKeyData);
|
||||
}
|
||||
iRetCode = SPI_execp(pplan, saPlanData, NULL, 1);
|
||||
|
||||
if(iRetCode != SPI_OK_INSERT ) {
|
||||
elog(NOTICE,"Error inserting row in pendingDelete");
|
||||
return -1;
|
||||
}
|
||||
if (cpKeyData != NULL)
|
||||
SPI_pfree(cpKeyData);
|
||||
|
||||
if (iRetCode != SPI_OK_INSERT)
|
||||
{
|
||||
elog(NOTICE, "Error inserting row in pendingDelete");
|
||||
return -1;
|
||||
}
|
||||
#if defined DEBUG_OUTPUT
|
||||
elog(NOTICE,"INSERT SUCCESFULL");
|
||||
elog(NOTICE, "INSERT SUCCESFULL");
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int2vector * getPrimaryKey(Oid tblOid) {
|
||||
char * queryBase;
|
||||
char * query;
|
||||
bool isNull;
|
||||
int2vector * resultKey;
|
||||
int2vector * tpResultKey;
|
||||
HeapTuple resTuple;
|
||||
Datum resDatum;
|
||||
int ret;
|
||||
queryBase = "SELECT indkey FROM pg_index WHERE indisprimary='t' AND indrelid=";
|
||||
query = SPI_palloc(strlen(queryBase) + MAX_OID_LEN+1);
|
||||
sprintf(query,"%s%d",queryBase,tblOid);
|
||||
ret = SPI_exec(query,1);
|
||||
if(ret != SPI_OK_SELECT || SPI_processed != 1 ) {
|
||||
elog(NOTICE,"Could not select primary index key");
|
||||
return NULL;
|
||||
}
|
||||
int2vector *
|
||||
getPrimaryKey(Oid tblOid)
|
||||
{
|
||||
char *queryBase;
|
||||
char *query;
|
||||
bool isNull;
|
||||
int2vector *resultKey;
|
||||
int2vector *tpResultKey;
|
||||
HeapTuple resTuple;
|
||||
Datum resDatum;
|
||||
int ret;
|
||||
|
||||
resTuple = SPI_tuptable->vals[0];
|
||||
resDatum = SPI_getbinval(resTuple,SPI_tuptable->tupdesc,1,&isNull);
|
||||
queryBase = "SELECT indkey FROM pg_index WHERE indisprimary='t' AND indrelid=";
|
||||
query = SPI_palloc(strlen(queryBase) + MAX_OID_LEN + 1);
|
||||
sprintf(query, "%s%d", queryBase, tblOid);
|
||||
ret = SPI_exec(query, 1);
|
||||
if (ret != SPI_OK_SELECT || SPI_processed != 1)
|
||||
{
|
||||
elog(NOTICE, "Could not select primary index key");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tpResultKey = (int2vector*) DatumGetPointer(resDatum);
|
||||
resultKey = SPI_palloc(sizeof(int2vector));
|
||||
memcpy(resultKey,tpResultKey,sizeof(int2vector));
|
||||
resTuple = SPI_tuptable->vals[0];
|
||||
resDatum = SPI_getbinval(resTuple, SPI_tuptable->tupdesc, 1, &isNull);
|
||||
|
||||
SPI_pfree(query);
|
||||
return resultKey;
|
||||
tpResultKey = (int2vector *) DatumGetPointer(resDatum);
|
||||
resultKey = SPI_palloc(sizeof(int2vector));
|
||||
memcpy(resultKey, tpResultKey, sizeof(int2vector));
|
||||
|
||||
SPI_pfree(query);
|
||||
return resultKey;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Stores a copy of the non-key data for the row.
|
||||
*****************************************************************************/
|
||||
int storeData(char * cpTableName,HeapTuple tTupleData,TupleDesc tTupleDesc,
|
||||
TriggerData * tpTrigData,int iIncludeKeyData) {
|
||||
int
|
||||
storeData(char *cpTableName, HeapTuple tTupleData, TupleDesc tTupleDesc,
|
||||
TriggerData *tpTrigData, int iIncludeKeyData)
|
||||
{
|
||||
|
||||
Oid planArgTypes[1] = {NAMEOID};
|
||||
char * insQuery = "INSERT INTO \"PendingData\" (\"SeqId\",\"IsKey\",\"Data\") VALUES(currval('\"Pending_SeqId_seq\"'),'f',$1)";
|
||||
void * pplan;
|
||||
Datum planData[1];
|
||||
char * cpKeyData;
|
||||
int iRetValue;
|
||||
Oid planArgTypes[1] = {NAMEOID};
|
||||
char *insQuery = "INSERT INTO \"PendingData\" (\"SeqId\",\"IsKey\",\"Data\") VALUES(currval('\"Pending_SeqId_seq\"'),'f',$1)";
|
||||
void *pplan;
|
||||
Datum planData[1];
|
||||
char *cpKeyData;
|
||||
int iRetValue;
|
||||
|
||||
pplan = SPI_prepare(insQuery,1,planArgTypes);
|
||||
if(pplan==NULL) {
|
||||
elog(NOTICE,"Could not prepare INSERT plan");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// pplan = SPI_saveplan(pplan);
|
||||
if(iIncludeKeyData==0) {
|
||||
cpKeyData = packageData(tTupleData, tTupleDesc,tpTrigData,NONPRIMARY);
|
||||
}
|
||||
else {
|
||||
cpKeyData = packageData(tTupleData,tTupleDesc,tpTrigData,ALL);
|
||||
}
|
||||
|
||||
planData[0] = PointerGetDatum(cpKeyData);
|
||||
iRetValue = SPI_execp(pplan,planData,NULL,1);
|
||||
|
||||
if(cpKeyData!=0) {
|
||||
SPI_pfree(cpKeyData);
|
||||
}
|
||||
pplan = SPI_prepare(insQuery, 1, planArgTypes);
|
||||
if (pplan == NULL)
|
||||
{
|
||||
elog(NOTICE, "Could not prepare INSERT plan");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(iRetValue != SPI_OK_INSERT ) {
|
||||
elog(NOTICE,"Error inserting row in pendingDelete");
|
||||
return -1;
|
||||
}
|
||||
/* pplan = SPI_saveplan(pplan); */
|
||||
if (iIncludeKeyData == 0)
|
||||
cpKeyData = packageData(tTupleData, tTupleDesc, tpTrigData, NONPRIMARY);
|
||||
else
|
||||
cpKeyData = packageData(tTupleData, tTupleDesc, tpTrigData, ALL);
|
||||
|
||||
planData[0] = PointerGetDatum(cpKeyData);
|
||||
iRetValue = SPI_execp(pplan, planData, NULL, 1);
|
||||
|
||||
if (cpKeyData != 0)
|
||||
SPI_pfree(cpKeyData);
|
||||
|
||||
if (iRetValue != SPI_OK_INSERT)
|
||||
{
|
||||
elog(NOTICE, "Error inserting row in pendingDelete");
|
||||
return -1;
|
||||
}
|
||||
#if defined DEBUG_OUTPUT
|
||||
elog(NOTICE,"INSERT SUCCESFULL");
|
||||
elog(NOTICE, "INSERT SUCCESFULL");
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Packages the data in tTupleData into a string of the format
|
||||
* Packages the data in tTupleData into a string of the format
|
||||
* FieldName='value text' where any quotes inside of value text
|
||||
* are escaped with a backslash and any backslashes in value text
|
||||
* are esacped by a second back slash.
|
||||
*
|
||||
* tTupleDesc should be a description of the tuple stored in
|
||||
* tTupleData.
|
||||
* tTupleDesc should be a description of the tuple stored in
|
||||
* tTupleData.
|
||||
*
|
||||
* eFieldUsage specifies which fields to use.
|
||||
* PRIMARY implies include only primary key fields.
|
||||
* NONPRIMARY implies include only non-primary key fields.
|
||||
* ALL implies include all fields.
|
||||
* PRIMARY implies include only primary key fields.
|
||||
* NONPRIMARY implies include only non-primary key fields.
|
||||
* ALL implies include all fields.
|
||||
*/
|
||||
char * packageData(HeapTuple tTupleData, TupleDesc tTupleDesc,
|
||||
TriggerData * tpTrigData,
|
||||
enum FieldUsage eKeyUsage ) {
|
||||
int iNumCols;
|
||||
int2vector * tpPKeys=NULL;
|
||||
int iColumnCounter;
|
||||
char * cpDataBlock;
|
||||
int iDataBlockSize;
|
||||
int iUsedDataBlock;
|
||||
|
||||
iNumCols = tTupleDesc->natts;
|
||||
char *
|
||||
packageData(HeapTuple tTupleData, TupleDesc tTupleDesc,
|
||||
TriggerData *tpTrigData,
|
||||
enum FieldUsage eKeyUsage)
|
||||
{
|
||||
int iNumCols;
|
||||
int2vector *tpPKeys = NULL;
|
||||
int iColumnCounter;
|
||||
char *cpDataBlock;
|
||||
int iDataBlockSize;
|
||||
int iUsedDataBlock;
|
||||
|
||||
if(eKeyUsage!=ALL) {
|
||||
tpPKeys = getPrimaryKey(tpTrigData->tg_relation->rd_id);
|
||||
if(tpPKeys==NULL) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
#if defined DEBUG_OUTPUT
|
||||
if(tpPKeys!=NULL) {
|
||||
elog(NOTICE,"Have primary keys");
|
||||
}
|
||||
#endif
|
||||
cpDataBlock = SPI_palloc(BUFFER_SIZE);
|
||||
iDataBlockSize = BUFFER_SIZE;
|
||||
iUsedDataBlock = 0; /* To account for the null */
|
||||
iNumCols = tTupleDesc->natts;
|
||||
|
||||
for(iColumnCounter=1; iColumnCounter <=iNumCols; iColumnCounter++) {
|
||||
int iIsPrimaryKey;
|
||||
int iPrimaryKeyIndex;
|
||||
char * cpUnFormatedPtr;
|
||||
char * cpFormatedPtr;
|
||||
|
||||
char * cpFieldName;
|
||||
char * cpFieldData;
|
||||
if(eKeyUsage!=ALL) {
|
||||
//Determine if this is a primary key or not.
|
||||
iIsPrimaryKey=0;
|
||||
for(iPrimaryKeyIndex=0; (*tpPKeys)[iPrimaryKeyIndex]!=0;
|
||||
iPrimaryKeyIndex++) {
|
||||
if((*tpPKeys)[iPrimaryKeyIndex]==iColumnCounter) {
|
||||
iIsPrimaryKey=1;
|
||||
break;
|
||||
if (eKeyUsage != ALL)
|
||||
{
|
||||
tpPKeys = getPrimaryKey(tpTrigData->tg_relation->rd_id);
|
||||
if (tpPKeys == NULL)
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
if( iIsPrimaryKey ? (eKeyUsage!=PRIMARY) : (eKeyUsage!=NONPRIMARY)) {
|
||||
/**
|
||||
* Don't use.
|
||||
*/
|
||||
#if defined DEBUG_OUTPUT
|
||||
elog(NOTICE,"Skipping column");
|
||||
if (tpPKeys != NULL)
|
||||
elog(NOTICE, "Have primary keys");
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
} /* KeyUsage!=ALL */
|
||||
cpFieldName = DatumGetPointer(NameGetDatum(&tTupleDesc->attrs
|
||||
[iColumnCounter-1]->attname));
|
||||
#if defined DEBUG_OUTPUT
|
||||
elog(NOTICE,cpFieldName);
|
||||
#endif
|
||||
while(iDataBlockSize - iUsedDataBlock < strlen(cpFieldName) +4) {
|
||||
cpDataBlock = SPI_repalloc(cpDataBlock,iDataBlockSize + BUFFER_SIZE);
|
||||
iDataBlockSize = iDataBlockSize + BUFFER_SIZE;
|
||||
}
|
||||
sprintf(cpDataBlock+iUsedDataBlock,"\"%s\"=",cpFieldName);
|
||||
iUsedDataBlock = iUsedDataBlock + strlen(cpFieldName)+3;
|
||||
cpFieldData=SPI_getvalue(tTupleData,tTupleDesc,iColumnCounter);
|
||||
|
||||
cpUnFormatedPtr = cpFieldData;
|
||||
cpFormatedPtr = cpDataBlock + iUsedDataBlock;
|
||||
if(cpFieldData!=NULL) {
|
||||
*cpFormatedPtr='\'';
|
||||
iUsedDataBlock++;
|
||||
cpFormatedPtr++;
|
||||
}
|
||||
else {
|
||||
*cpFormatedPtr=' ';
|
||||
iUsedDataBlock++;
|
||||
cpFormatedPtr++;
|
||||
continue;
|
||||
|
||||
}
|
||||
#if defined DEBUG_OUTPUT
|
||||
elog(NOTICE,cpFieldData);
|
||||
elog(NOTICE,"Starting format loop");
|
||||
#endif
|
||||
while(*cpUnFormatedPtr!=0) {
|
||||
while(iDataBlockSize - iUsedDataBlock < 2) {
|
||||
cpDataBlock = SPI_repalloc(cpDataBlock,iDataBlockSize+BUFFER_SIZE);
|
||||
iDataBlockSize = iDataBlockSize + BUFFER_SIZE;
|
||||
cpFormatedPtr = cpDataBlock + iUsedDataBlock;
|
||||
}
|
||||
if(*cpUnFormatedPtr=='\\' || *cpUnFormatedPtr=='\'') {
|
||||
*cpFormatedPtr='\\';
|
||||
cpFormatedPtr++;
|
||||
iUsedDataBlock++;
|
||||
}
|
||||
*cpFormatedPtr=*cpUnFormatedPtr;
|
||||
cpFormatedPtr++;
|
||||
cpUnFormatedPtr++;
|
||||
iUsedDataBlock++;
|
||||
}
|
||||
cpDataBlock = SPI_palloc(BUFFER_SIZE);
|
||||
iDataBlockSize = BUFFER_SIZE;
|
||||
iUsedDataBlock = 0; /* To account for the null */
|
||||
|
||||
SPI_pfree(cpFieldData);
|
||||
for (iColumnCounter = 1; iColumnCounter <= iNumCols; iColumnCounter++)
|
||||
{
|
||||
int iIsPrimaryKey;
|
||||
int iPrimaryKeyIndex;
|
||||
char *cpUnFormatedPtr;
|
||||
char *cpFormatedPtr;
|
||||
|
||||
while(iDataBlockSize - iUsedDataBlock < 3) {
|
||||
cpDataBlock = SPI_repalloc(cpDataBlock,iDataBlockSize+BUFFER_SIZE);
|
||||
iDataBlockSize = iDataBlockSize + BUFFER_SIZE;
|
||||
cpFormatedPtr = cpDataBlock + iUsedDataBlock;
|
||||
}
|
||||
sprintf(cpFormatedPtr,"' ");
|
||||
iUsedDataBlock = iUsedDataBlock +2;
|
||||
#if defined DEBUG_OUTPUT
|
||||
elog(NOTICE,cpDataBlock);
|
||||
#endif
|
||||
|
||||
} /* for iColumnCounter */
|
||||
if(tpPKeys!=NULL) {
|
||||
SPI_pfree(tpPKeys);
|
||||
}
|
||||
#if defined DEBUG_OUTPUT
|
||||
elog(NOTICE,"Returning");
|
||||
#endif
|
||||
memset(cpDataBlock + iUsedDataBlock,0,iDataBlockSize - iUsedDataBlock);
|
||||
char *cpFieldName;
|
||||
char *cpFieldData;
|
||||
|
||||
if (eKeyUsage != ALL)
|
||||
{
|
||||
/* Determine if this is a primary key or not. */
|
||||
iIsPrimaryKey = 0;
|
||||
for (iPrimaryKeyIndex = 0; (*tpPKeys)[iPrimaryKeyIndex] != 0;
|
||||
iPrimaryKeyIndex++)
|
||||
{
|
||||
if ((*tpPKeys)[iPrimaryKeyIndex] == iColumnCounter)
|
||||
{
|
||||
iIsPrimaryKey = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (iIsPrimaryKey ? (eKeyUsage != PRIMARY) : (eKeyUsage != NONPRIMARY))
|
||||
{
|
||||
/**
|
||||
* Don't use.
|
||||
*/
|
||||
#if defined DEBUG_OUTPUT
|
||||
elog(NOTICE, "Skipping column");
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
} /* KeyUsage!=ALL */
|
||||
cpFieldName = DatumGetPointer(NameGetDatum(&tTupleDesc->attrs
|
||||
[iColumnCounter - 1]->attname));
|
||||
#if defined DEBUG_OUTPUT
|
||||
elog(NOTICE, cpFieldName);
|
||||
#endif
|
||||
while (iDataBlockSize - iUsedDataBlock < strlen(cpFieldName) + 4)
|
||||
{
|
||||
cpDataBlock = SPI_repalloc(cpDataBlock, iDataBlockSize + BUFFER_SIZE);
|
||||
iDataBlockSize = iDataBlockSize + BUFFER_SIZE;
|
||||
}
|
||||
sprintf(cpDataBlock + iUsedDataBlock, "\"%s\"=", cpFieldName);
|
||||
iUsedDataBlock = iUsedDataBlock + strlen(cpFieldName) + 3;
|
||||
cpFieldData = SPI_getvalue(tTupleData, tTupleDesc, iColumnCounter);
|
||||
|
||||
cpUnFormatedPtr = cpFieldData;
|
||||
cpFormatedPtr = cpDataBlock + iUsedDataBlock;
|
||||
if (cpFieldData != NULL)
|
||||
{
|
||||
*cpFormatedPtr = '\'';
|
||||
iUsedDataBlock++;
|
||||
cpFormatedPtr++;
|
||||
}
|
||||
else
|
||||
{
|
||||
*cpFormatedPtr = ' ';
|
||||
iUsedDataBlock++;
|
||||
cpFormatedPtr++;
|
||||
continue;
|
||||
|
||||
}
|
||||
#if defined DEBUG_OUTPUT
|
||||
elog(NOTICE, cpFieldData);
|
||||
elog(NOTICE, "Starting format loop");
|
||||
#endif
|
||||
while (*cpUnFormatedPtr != 0)
|
||||
{
|
||||
while (iDataBlockSize - iUsedDataBlock < 2)
|
||||
{
|
||||
cpDataBlock = SPI_repalloc(cpDataBlock, iDataBlockSize + BUFFER_SIZE);
|
||||
iDataBlockSize = iDataBlockSize + BUFFER_SIZE;
|
||||
cpFormatedPtr = cpDataBlock + iUsedDataBlock;
|
||||
}
|
||||
if (*cpUnFormatedPtr == '\\' || *cpUnFormatedPtr == '\'')
|
||||
{
|
||||
*cpFormatedPtr = '\\';
|
||||
cpFormatedPtr++;
|
||||
iUsedDataBlock++;
|
||||
}
|
||||
*cpFormatedPtr = *cpUnFormatedPtr;
|
||||
cpFormatedPtr++;
|
||||
cpUnFormatedPtr++;
|
||||
iUsedDataBlock++;
|
||||
}
|
||||
|
||||
SPI_pfree(cpFieldData);
|
||||
|
||||
while (iDataBlockSize - iUsedDataBlock < 3)
|
||||
{
|
||||
cpDataBlock = SPI_repalloc(cpDataBlock, iDataBlockSize + BUFFER_SIZE);
|
||||
iDataBlockSize = iDataBlockSize + BUFFER_SIZE;
|
||||
cpFormatedPtr = cpDataBlock + iUsedDataBlock;
|
||||
}
|
||||
sprintf(cpFormatedPtr, "' ");
|
||||
iUsedDataBlock = iUsedDataBlock + 2;
|
||||
#if defined DEBUG_OUTPUT
|
||||
elog(NOTICE, cpDataBlock);
|
||||
#endif
|
||||
|
||||
} /* for iColumnCounter */
|
||||
if (tpPKeys != NULL)
|
||||
SPI_pfree(tpPKeys);
|
||||
#if defined DEBUG_OUTPUT
|
||||
elog(NOTICE, "Returning");
|
||||
#endif
|
||||
memset(cpDataBlock + iUsedDataBlock, 0, iDataBlockSize - iUsedDataBlock);
|
||||
|
||||
return cpDataBlock;
|
||||
|
||||
return cpDataBlock;
|
||||
|
||||
}
|
||||
|
@@ -37,7 +37,7 @@ psnprintf(size_t len, const char *fmt,...)
|
||||
|
||||
PG_FUNCTION_INFO_V1(database_size);
|
||||
|
||||
Datum database_size(PG_FUNCTION_ARGS);
|
||||
Datum database_size(PG_FUNCTION_ARGS);
|
||||
|
||||
Datum
|
||||
database_size(PG_FUNCTION_ARGS)
|
||||
@@ -97,7 +97,7 @@ database_size(PG_FUNCTION_ARGS)
|
||||
|
||||
PG_FUNCTION_INFO_V1(relation_size);
|
||||
|
||||
Datum relation_size(PG_FUNCTION_ARGS);
|
||||
Datum relation_size(PG_FUNCTION_ARGS);
|
||||
|
||||
Datum
|
||||
relation_size(PG_FUNCTION_ARGS)
|
||||
@@ -111,7 +111,7 @@ relation_size(PG_FUNCTION_ARGS)
|
||||
unsigned int segcount;
|
||||
|
||||
relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname,
|
||||
"relation_size"));
|
||||
"relation_size"));
|
||||
relation = heap_openrv(relrv, AccessShareLock);
|
||||
|
||||
relnode = relation->rd_rel->relfilenode;
|
||||
|
@@ -73,7 +73,7 @@ main(int argc, char **argv)
|
||||
SELECT count(*)::int4 \
|
||||
FROM \"%s\" t1, \"%s\" t2 \
|
||||
WHERE t1.\"%s\" = t2.oid ",
|
||||
relname, relname2, attname);
|
||||
relname, relname2, attname);
|
||||
else
|
||||
sprintf(query, 4000, "\
|
||||
DECLARE c_matches BINARY CURSOR FOR \
|
||||
|
@@ -11,7 +11,7 @@
|
||||
* This file is the property of the Digital Music Network (DMN).
|
||||
* It is being made available to users of the PostgreSQL system
|
||||
* under the BSD license.
|
||||
*
|
||||
*
|
||||
* NOTE: This module requires sizeof(void *) to be the same as sizeof(int)
|
||||
*/
|
||||
#include "postgres.h"
|
||||
@@ -45,31 +45,31 @@
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ArrayType a;
|
||||
int items;
|
||||
int lower;
|
||||
int4 array[1];
|
||||
}PGARRAY;
|
||||
ArrayType a;
|
||||
int items;
|
||||
int lower;
|
||||
int4 array[1];
|
||||
} PGARRAY;
|
||||
|
||||
/* This is used to keep track of our position during enumeration */
|
||||
typedef struct callContext
|
||||
{
|
||||
PGARRAY *p;
|
||||
int num;
|
||||
int flags;
|
||||
}CTX;
|
||||
PGARRAY *p;
|
||||
int num;
|
||||
int flags;
|
||||
} CTX;
|
||||
|
||||
#define TOASTED 1
|
||||
#define START_NUM 8
|
||||
#define START_NUM 8
|
||||
#define PGARRAY_SIZE(n) (sizeof(PGARRAY) + ((n-1)*sizeof(int4)))
|
||||
|
||||
static PGARRAY * GetPGArray(int4 state, int fAdd);
|
||||
static PGARRAY *ShrinkPGArray(PGARRAY *p);
|
||||
static PGARRAY *GetPGArray(int4 state, int fAdd);
|
||||
static PGARRAY *ShrinkPGArray(PGARRAY * p);
|
||||
|
||||
Datum int_agg_state(PG_FUNCTION_ARGS);
|
||||
Datum int_agg_final_count(PG_FUNCTION_ARGS);
|
||||
Datum int_agg_final_array(PG_FUNCTION_ARGS);
|
||||
Datum int_enum(PG_FUNCTION_ARGS);
|
||||
Datum int_agg_state(PG_FUNCTION_ARGS);
|
||||
Datum int_agg_final_count(PG_FUNCTION_ARGS);
|
||||
Datum int_agg_final_array(PG_FUNCTION_ARGS);
|
||||
Datum int_enum(PG_FUNCTION_ARGS);
|
||||
|
||||
PG_FUNCTION_INFO_V1(int_agg_state);
|
||||
PG_FUNCTION_INFO_V1(int_agg_final_count);
|
||||
@@ -80,20 +80,21 @@ PG_FUNCTION_INFO_V1(int_enum);
|
||||
* Manage the aggregation state of the array
|
||||
* You need to specify the correct memory context, or it will vanish!
|
||||
*/
|
||||
static PGARRAY * GetPGArray(int4 state, int fAdd)
|
||||
static PGARRAY *
|
||||
GetPGArray(int4 state, int fAdd)
|
||||
{
|
||||
PGARRAY *p = (PGARRAY *) state;
|
||||
PGARRAY *p = (PGARRAY *) state;
|
||||
|
||||
if(!state)
|
||||
if (!state)
|
||||
{
|
||||
/* New array */
|
||||
int cb = PGARRAY_SIZE(START_NUM);
|
||||
int cb = PGARRAY_SIZE(START_NUM);
|
||||
|
||||
p = (PGARRAY *) MemoryContextAlloc(TopTransactionContext, cb);
|
||||
|
||||
if(!p)
|
||||
if (!p)
|
||||
{
|
||||
elog(ERROR,"Integer aggregator, cant allocate TopTransactionContext memory");
|
||||
elog(ERROR, "Integer aggregator, cant allocate TopTransactionContext memory");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -104,22 +105,22 @@ static PGARRAY * GetPGArray(int4 state, int fAdd)
|
||||
p->a.elemtype = INT4OID;
|
||||
#endif
|
||||
p->items = 0;
|
||||
p->lower= START_NUM;
|
||||
p->lower = START_NUM;
|
||||
}
|
||||
else if(fAdd)
|
||||
{ /* Ensure array has space */
|
||||
if(p->items >= p->lower)
|
||||
else if (fAdd)
|
||||
{ /* Ensure array has space */
|
||||
if (p->items >= p->lower)
|
||||
{
|
||||
PGARRAY *pn;
|
||||
int n = p->lower + p->lower;
|
||||
int cbNew = PGARRAY_SIZE(n);
|
||||
PGARRAY *pn;
|
||||
int n = p->lower + p->lower;
|
||||
int cbNew = PGARRAY_SIZE(n);
|
||||
|
||||
pn = (PGARRAY *) repalloc(p, cbNew);
|
||||
|
||||
if(!pn)
|
||||
{ /* Realloc failed! Reallocate new block. */
|
||||
if (!pn)
|
||||
{ /* Realloc failed! Reallocate new block. */
|
||||
pn = (PGARRAY *) MemoryContextAlloc(TopTransactionContext, cbNew);
|
||||
if(!pn)
|
||||
if (!pn)
|
||||
{
|
||||
elog(ERROR, "Integer aggregator, REALLY REALLY can't alloc memory");
|
||||
return (PGARRAY *) NULL;
|
||||
@@ -136,24 +137,29 @@ static PGARRAY * GetPGArray(int4 state, int fAdd)
|
||||
}
|
||||
|
||||
/* Shrinks the array to its actual size and moves it into the standard
|
||||
* memory allocation context, frees working memory */
|
||||
static PGARRAY *ShrinkPGArray(PGARRAY *p)
|
||||
* memory allocation context, frees working memory */
|
||||
static PGARRAY *
|
||||
ShrinkPGArray(PGARRAY * p)
|
||||
{
|
||||
PGARRAY *pnew=NULL;
|
||||
if(p)
|
||||
PGARRAY *pnew = NULL;
|
||||
|
||||
if (p)
|
||||
{
|
||||
/* get target size */
|
||||
int cb = PGARRAY_SIZE(p->items);
|
||||
int cb = PGARRAY_SIZE(p->items);
|
||||
|
||||
/* use current transaction context */
|
||||
pnew = palloc(cb);
|
||||
|
||||
if(pnew)
|
||||
if (pnew)
|
||||
{
|
||||
/* Fix up the fields in the new structure, so Postgres understands */
|
||||
/*
|
||||
* Fix up the fields in the new structure, so Postgres
|
||||
* understands
|
||||
*/
|
||||
memcpy(pnew, p, cb);
|
||||
pnew->a.size = cb;
|
||||
pnew->a.ndim=1;
|
||||
pnew->a.ndim = 1;
|
||||
pnew->a.flags = 0;
|
||||
#ifndef PG_7_2
|
||||
pnew->a.elemtype = INT4OID;
|
||||
@@ -161,79 +167,72 @@ static PGARRAY *ShrinkPGArray(PGARRAY *p)
|
||||
pnew->lower = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
elog(ERROR, "Integer aggregator, can't allocate memory");
|
||||
}
|
||||
pfree(p);
|
||||
}
|
||||
return pnew;
|
||||
}
|
||||
|
||||
/* Called for each iteration during an aggregate function */
|
||||
Datum int_agg_state(PG_FUNCTION_ARGS)
|
||||
Datum
|
||||
int_agg_state(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int4 state = PG_GETARG_INT32(0);
|
||||
int4 value = PG_GETARG_INT32(1);
|
||||
int4 state = PG_GETARG_INT32(0);
|
||||
int4 value = PG_GETARG_INT32(1);
|
||||
|
||||
PGARRAY *p = GetPGArray(state, 1);
|
||||
if(!p)
|
||||
{
|
||||
elog(ERROR,"No aggregate storage");
|
||||
}
|
||||
else if(p->items >= p->lower)
|
||||
{
|
||||
elog(ERROR,"aggregate storage too small");
|
||||
}
|
||||
PGARRAY *p = GetPGArray(state, 1);
|
||||
|
||||
if (!p)
|
||||
elog(ERROR, "No aggregate storage");
|
||||
else if (p->items >= p->lower)
|
||||
elog(ERROR, "aggregate storage too small");
|
||||
else
|
||||
{
|
||||
p->array[p->items++]= value;
|
||||
}
|
||||
p->array[p->items++] = value;
|
||||
PG_RETURN_INT32(p);
|
||||
}
|
||||
|
||||
/* This is the final function used for the integer aggregator. It returns all the integers
|
||||
* collected as a one dimentional integer array */
|
||||
Datum int_agg_final_array(PG_FUNCTION_ARGS)
|
||||
Datum
|
||||
int_agg_final_array(PG_FUNCTION_ARGS)
|
||||
{
|
||||
PGARRAY *pnew = ShrinkPGArray(GetPGArray(PG_GETARG_INT32(0),0));
|
||||
if(pnew)
|
||||
{
|
||||
PGARRAY *pnew = ShrinkPGArray(GetPGArray(PG_GETARG_INT32(0), 0));
|
||||
|
||||
if (pnew)
|
||||
PG_RETURN_POINTER(pnew);
|
||||
}
|
||||
else
|
||||
{
|
||||
PG_RETURN_NULL();
|
||||
}
|
||||
}
|
||||
|
||||
/* This function accepts an array, and returns one item for each entry in the array */
|
||||
Datum int_enum(PG_FUNCTION_ARGS)
|
||||
Datum
|
||||
int_enum(PG_FUNCTION_ARGS)
|
||||
{
|
||||
PGARRAY *p = (PGARRAY *) PG_GETARG_POINTER(0);
|
||||
CTX *pc;
|
||||
ReturnSetInfo *rsi = (ReturnSetInfo *)fcinfo->resultinfo;
|
||||
PGARRAY *p = (PGARRAY *) PG_GETARG_POINTER(0);
|
||||
CTX *pc;
|
||||
ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
|
||||
|
||||
if (!rsi || !IsA(rsi, ReturnSetInfo))
|
||||
elog(ERROR, "No ReturnSetInfo sent! function must be declared returning a 'setof' integer");
|
||||
|
||||
if(!p)
|
||||
if (!p)
|
||||
{
|
||||
elog(WARNING, "No data sent");
|
||||
PG_RETURN_NULL();
|
||||
}
|
||||
|
||||
if(!fcinfo->context)
|
||||
if (!fcinfo->context)
|
||||
{
|
||||
/* Allocate a working context */
|
||||
pc = (CTX *) palloc(sizeof(CTX));
|
||||
|
||||
/* Don't copy atribute if you don't need too */
|
||||
if(VARATT_IS_EXTENDED(p) )
|
||||
if (VARATT_IS_EXTENDED(p))
|
||||
{
|
||||
/* Toasted!!! */
|
||||
pc->p = (PGARRAY *) PG_DETOAST_DATUM_COPY(p);
|
||||
pc->flags = TOASTED;
|
||||
if(!pc->p)
|
||||
if (!pc->p)
|
||||
{
|
||||
elog(ERROR, "Error in toaster!!! no detoasting");
|
||||
PG_RETURN_NULL();
|
||||
@@ -246,25 +245,26 @@ Datum int_enum(PG_FUNCTION_ARGS)
|
||||
pc->flags = 0;
|
||||
}
|
||||
fcinfo->context = (Node *) pc;
|
||||
pc->num=0;
|
||||
pc->num = 0;
|
||||
}
|
||||
else /* use an existing one */
|
||||
{
|
||||
else
|
||||
/* use an existing one */
|
||||
pc = (CTX *) fcinfo->context;
|
||||
}
|
||||
/* Are we done yet? */
|
||||
if(pc->num >= pc->p->items)
|
||||
if (pc->num >= pc->p->items)
|
||||
{
|
||||
/* We are done */
|
||||
if(pc->flags & TOASTED)
|
||||
if (pc->flags & TOASTED)
|
||||
pfree(pc->p);
|
||||
pfree(fcinfo->context);
|
||||
fcinfo->context = NULL;
|
||||
rsi->isDone = ExprEndResult ;
|
||||
rsi->isDone = ExprEndResult;
|
||||
}
|
||||
else /* nope, return the next value */
|
||||
else
|
||||
/* nope, return the next value */
|
||||
{
|
||||
int val = pc->p->array[pc->num++];
|
||||
int val = pc->p->array[pc->num++];
|
||||
|
||||
rsi->isDone = ExprMultipleResult;
|
||||
PG_RETURN_INT32(val);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* GiST support for ltree[]
|
||||
* GiST support for ltree[]
|
||||
* Teodor Sigaev <teodor@stack.net>
|
||||
*/
|
||||
|
||||
@@ -11,22 +11,27 @@
|
||||
|
||||
#include "crc32.h"
|
||||
|
||||
PG_FUNCTION_INFO_V1( _ltree_compress );
|
||||
Datum _ltree_compress(PG_FUNCTION_ARGS);
|
||||
PG_FUNCTION_INFO_V1( _ltree_same );
|
||||
Datum _ltree_same(PG_FUNCTION_ARGS);
|
||||
PG_FUNCTION_INFO_V1( _ltree_union );
|
||||
Datum _ltree_union(PG_FUNCTION_ARGS);
|
||||
PG_FUNCTION_INFO_V1( _ltree_penalty );
|
||||
Datum _ltree_penalty(PG_FUNCTION_ARGS);
|
||||
PG_FUNCTION_INFO_V1( _ltree_picksplit );
|
||||
Datum _ltree_picksplit(PG_FUNCTION_ARGS);
|
||||
PG_FUNCTION_INFO_V1( _ltree_consistent );
|
||||
Datum _ltree_consistent(PG_FUNCTION_ARGS);
|
||||
PG_FUNCTION_INFO_V1(_ltree_compress);
|
||||
Datum _ltree_compress(PG_FUNCTION_ARGS);
|
||||
|
||||
PG_FUNCTION_INFO_V1(_ltree_same);
|
||||
Datum _ltree_same(PG_FUNCTION_ARGS);
|
||||
|
||||
PG_FUNCTION_INFO_V1(_ltree_union);
|
||||
Datum _ltree_union(PG_FUNCTION_ARGS);
|
||||
|
||||
PG_FUNCTION_INFO_V1(_ltree_penalty);
|
||||
Datum _ltree_penalty(PG_FUNCTION_ARGS);
|
||||
|
||||
PG_FUNCTION_INFO_V1(_ltree_picksplit);
|
||||
Datum _ltree_picksplit(PG_FUNCTION_ARGS);
|
||||
|
||||
PG_FUNCTION_INFO_V1(_ltree_consistent);
|
||||
Datum _ltree_consistent(PG_FUNCTION_ARGS);
|
||||
|
||||
#define GETENTRY(vec,pos) ((ltree_gist *) DatumGetPointer(((GISTENTRY *) VARDATA(vec))[(pos)].key))
|
||||
#define NEXTVAL(x) ( (ltree*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
|
||||
#define SUMBIT(val) ( \
|
||||
#define SUMBIT(val) ( \
|
||||
GETBITBYTE(val,0) + \
|
||||
GETBITBYTE(val,1) + \
|
||||
GETBITBYTE(val,2) + \
|
||||
@@ -34,235 +39,280 @@ Datum _ltree_consistent(PG_FUNCTION_ARGS);
|
||||
GETBITBYTE(val,4) + \
|
||||
GETBITBYTE(val,5) + \
|
||||
GETBITBYTE(val,6) + \
|
||||
GETBITBYTE(val,7) \
|
||||
GETBITBYTE(val,7) \
|
||||
)
|
||||
#define WISH_F(a,b,c) (double)( -(double)(((a)-(b))*((a)-(b))*((a)-(b)))*(c) )
|
||||
|
||||
static void
|
||||
hashing(BITVECP sign, ltree *t) {
|
||||
int tlen = t->numlevel;
|
||||
hashing(BITVECP sign, ltree * t)
|
||||
{
|
||||
int tlen = t->numlevel;
|
||||
ltree_level *cur = LTREE_FIRST(t);
|
||||
int hash;
|
||||
int hash;
|
||||
|
||||
while(tlen > 0) {
|
||||
hash = ltree_crc32_sz( cur->name, cur->len );
|
||||
AHASH( sign, hash );
|
||||
while (tlen > 0)
|
||||
{
|
||||
hash = ltree_crc32_sz(cur->name, cur->len);
|
||||
AHASH(sign, hash);
|
||||
cur = LEVEL_NEXT(cur);
|
||||
tlen--;
|
||||
}
|
||||
}
|
||||
|
||||
Datum
|
||||
_ltree_compress(PG_FUNCTION_ARGS) {
|
||||
GISTENTRY *entry = (GISTENTRY *)PG_GETARG_POINTER(0);
|
||||
Datum
|
||||
_ltree_compress(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
|
||||
GISTENTRY *retval = entry;
|
||||
|
||||
if ( entry->leafkey ) { /* ltree */
|
||||
ltree_gist *key;
|
||||
ArrayType *val = DatumGetArrayTypeP(entry->key);
|
||||
int4 len = LTG_HDRSIZE + ASIGLEN;
|
||||
int num=ArrayGetNItems( ARR_NDIM(val), ARR_DIMS(val) );
|
||||
ltree *item = (ltree*)ARR_DATA_PTR(val);
|
||||
if (entry->leafkey)
|
||||
{ /* ltree */
|
||||
ltree_gist *key;
|
||||
ArrayType *val = DatumGetArrayTypeP(entry->key);
|
||||
int4 len = LTG_HDRSIZE + ASIGLEN;
|
||||
int num = ArrayGetNItems(ARR_NDIM(val), ARR_DIMS(val));
|
||||
ltree *item = (ltree *) ARR_DATA_PTR(val);
|
||||
|
||||
if ( ARR_NDIM(val) != 1 )
|
||||
elog(ERROR,"Dimension of array != 1");
|
||||
if (ARR_NDIM(val) != 1)
|
||||
elog(ERROR, "Dimension of array != 1");
|
||||
|
||||
key = (ltree_gist*)palloc( len );
|
||||
key = (ltree_gist *) palloc(len);
|
||||
key->len = len;
|
||||
key->flag = 0;
|
||||
|
||||
MemSet( LTG_SIGN(key), 0, sizeof(ASIGLEN) );
|
||||
while( num>0 ) {
|
||||
MemSet(LTG_SIGN(key), 0, sizeof(ASIGLEN));
|
||||
while (num > 0)
|
||||
{
|
||||
hashing(LTG_SIGN(key), item);
|
||||
num--;
|
||||
item = NEXTVAL(item);
|
||||
}
|
||||
|
||||
if ( PointerGetDatum(val) != entry->key )
|
||||
if (PointerGetDatum(val) != entry->key)
|
||||
pfree(val);
|
||||
|
||||
retval = (GISTENTRY*)palloc( sizeof(GISTENTRY) );
|
||||
retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
|
||||
gistentryinit(*retval, PointerGetDatum(key),
|
||||
entry->rel, entry->page,
|
||||
entry->offset, key->len, FALSE);
|
||||
} else {
|
||||
int4 i,len;
|
||||
ltree_gist *key;
|
||||
entry->rel, entry->page,
|
||||
entry->offset, key->len, FALSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
int4 i,
|
||||
len;
|
||||
ltree_gist *key;
|
||||
|
||||
BITVECP sign = LTG_SIGN(DatumGetPointer( entry->key ) );
|
||||
BITVECP sign = LTG_SIGN(DatumGetPointer(entry->key));
|
||||
|
||||
ALOOPBYTE(
|
||||
if ( sign[i] != 0xff )
|
||||
PG_RETURN_POINTER(retval);
|
||||
if (sign[i] != 0xff)
|
||||
PG_RETURN_POINTER(retval);
|
||||
);
|
||||
|
||||
len = LTG_HDRSIZE;
|
||||
key = (ltree_gist*)palloc( len );
|
||||
len = LTG_HDRSIZE;
|
||||
key = (ltree_gist *) palloc(len);
|
||||
key->len = len;
|
||||
key->flag = LTG_ALLTRUE;
|
||||
|
||||
retval = (GISTENTRY*)palloc( sizeof(GISTENTRY) );
|
||||
retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
|
||||
gistentryinit(*retval, PointerGetDatum(key),
|
||||
entry->rel, entry->page,
|
||||
entry->offset, key->len, FALSE);
|
||||
entry->rel, entry->page,
|
||||
entry->offset, key->len, FALSE);
|
||||
}
|
||||
PG_RETURN_POINTER(retval);
|
||||
}
|
||||
|
||||
Datum
|
||||
_ltree_same(PG_FUNCTION_ARGS) {
|
||||
ltree_gist* a=(ltree_gist*)PG_GETARG_POINTER(0);
|
||||
ltree_gist* b=(ltree_gist*)PG_GETARG_POINTER(1);
|
||||
bool *result = (bool *)PG_GETARG_POINTER(2);
|
||||
Datum
|
||||
_ltree_same(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ltree_gist *a = (ltree_gist *) PG_GETARG_POINTER(0);
|
||||
ltree_gist *b = (ltree_gist *) PG_GETARG_POINTER(1);
|
||||
bool *result = (bool *) PG_GETARG_POINTER(2);
|
||||
|
||||
if ( LTG_ISALLTRUE(a) && LTG_ISALLTRUE(b) ) {
|
||||
if (LTG_ISALLTRUE(a) && LTG_ISALLTRUE(b))
|
||||
*result = true;
|
||||
} else if ( LTG_ISALLTRUE(a) ) {
|
||||
else if (LTG_ISALLTRUE(a))
|
||||
*result = false;
|
||||
} else if ( LTG_ISALLTRUE(b) ) {
|
||||
else if (LTG_ISALLTRUE(b))
|
||||
*result = false;
|
||||
} else {
|
||||
int4 i;
|
||||
BITVECP sa=LTG_SIGN(a), sb=LTG_SIGN(b);
|
||||
else
|
||||
{
|
||||
int4 i;
|
||||
BITVECP sa = LTG_SIGN(a),
|
||||
sb = LTG_SIGN(b);
|
||||
|
||||
*result = true;
|
||||
ALOOPBYTE(
|
||||
if ( sa[i] != sb[i] ) {
|
||||
*result = false;
|
||||
break;
|
||||
}
|
||||
if (sa[i] != sb[i])
|
||||
{
|
||||
*result = false;
|
||||
break;
|
||||
}
|
||||
);
|
||||
}
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
static int4
|
||||
unionkey( BITVECP sbase, ltree_gist *add ) {
|
||||
int4 i;
|
||||
BITVECP sadd = LTG_SIGN( add );
|
||||
static int4
|
||||
unionkey(BITVECP sbase, ltree_gist * add)
|
||||
{
|
||||
int4 i;
|
||||
BITVECP sadd = LTG_SIGN(add);
|
||||
|
||||
if ( LTG_ISALLTRUE(add) )
|
||||
if (LTG_ISALLTRUE(add))
|
||||
return 1;
|
||||
|
||||
ALOOPBYTE(
|
||||
sbase[i] |= sadd[i];
|
||||
sbase[i] |= sadd[i];
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Datum
|
||||
_ltree_union(PG_FUNCTION_ARGS) {
|
||||
bytea *entryvec = (bytea *) PG_GETARG_POINTER(0);
|
||||
int *size = (int *) PG_GETARG_POINTER(1);
|
||||
ABITVEC base;
|
||||
int4 len = (VARSIZE(entryvec) - VARHDRSZ) / sizeof(GISTENTRY);
|
||||
int4 i;
|
||||
int4 flag = 0;
|
||||
ltree_gist *result;
|
||||
Datum
|
||||
_ltree_union(PG_FUNCTION_ARGS)
|
||||
{
|
||||
bytea *entryvec = (bytea *) PG_GETARG_POINTER(0);
|
||||
int *size = (int *) PG_GETARG_POINTER(1);
|
||||
ABITVEC base;
|
||||
int4 len = (VARSIZE(entryvec) - VARHDRSZ) / sizeof(GISTENTRY);
|
||||
int4 i;
|
||||
int4 flag = 0;
|
||||
ltree_gist *result;
|
||||
|
||||
MemSet( (void*)base, 0, sizeof(ABITVEC) );
|
||||
for(i=0;i<len;i++) {
|
||||
if ( unionkey( base, GETENTRY(entryvec, i) ) ) {
|
||||
MemSet((void *) base, 0, sizeof(ABITVEC));
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
if (unionkey(base, GETENTRY(entryvec, i)))
|
||||
{
|
||||
flag = LTG_ALLTRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
len = LTG_HDRSIZE + ( ( flag & LTG_ALLTRUE ) ? 0 : ASIGLEN );
|
||||
result = (ltree_gist*)palloc( len );
|
||||
len = LTG_HDRSIZE + ((flag & LTG_ALLTRUE) ? 0 : ASIGLEN);
|
||||
result = (ltree_gist *) palloc(len);
|
||||
*size = result->len = len;
|
||||
result->flag = flag;
|
||||
if ( ! LTG_ISALLTRUE(result) )
|
||||
memcpy((void*)LTG_SIGN(result), (void*)base, sizeof( ABITVEC ) );
|
||||
if (!LTG_ISALLTRUE(result))
|
||||
memcpy((void *) LTG_SIGN(result), (void *) base, sizeof(ABITVEC));
|
||||
|
||||
PG_RETURN_POINTER(result);
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
static int4
|
||||
sizebitvec( BITVECP sign ) {
|
||||
int4 size=0, i;
|
||||
sizebitvec(BITVECP sign)
|
||||
{
|
||||
int4 size = 0,
|
||||
i;
|
||||
|
||||
ALOOPBYTE(
|
||||
size += SUMBIT(*(char*)sign);
|
||||
sign = (BITVECP) ( ((char*)sign) + 1 );
|
||||
size += SUMBIT(*(char *) sign);
|
||||
sign = (BITVECP) (((char *) sign) + 1);
|
||||
);
|
||||
return size;
|
||||
}
|
||||
|
||||
Datum
|
||||
_ltree_penalty(PG_FUNCTION_ARGS) {
|
||||
ltree_gist *origval = (ltree_gist*)DatumGetPointer( ( (GISTENTRY *)PG_GETARG_POINTER(0) )->key );
|
||||
ltree_gist *newval = (ltree_gist*)DatumGetPointer( ( (GISTENTRY *)PG_GETARG_POINTER(1) )->key );
|
||||
float *penalty = (float *) PG_GETARG_POINTER(2);
|
||||
BITVECP orig = LTG_SIGN(origval);
|
||||
Datum
|
||||
_ltree_penalty(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ltree_gist *origval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(0))->key);
|
||||
ltree_gist *newval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(1))->key);
|
||||
float *penalty = (float *) PG_GETARG_POINTER(2);
|
||||
BITVECP orig = LTG_SIGN(origval);
|
||||
|
||||
if ( LTG_ISALLTRUE(origval) ) {
|
||||
if (LTG_ISALLTRUE(origval))
|
||||
{
|
||||
*penalty = 0.0;
|
||||
PG_RETURN_POINTER( penalty );
|
||||
PG_RETURN_POINTER(penalty);
|
||||
}
|
||||
|
||||
if ( LTG_ISALLTRUE(newval) ) {
|
||||
*penalty = (float) (ASIGLENBIT - sizebitvec( orig ) );
|
||||
} else {
|
||||
if (LTG_ISALLTRUE(newval))
|
||||
*penalty = (float) (ASIGLENBIT - sizebitvec(orig));
|
||||
else
|
||||
{
|
||||
unsigned char valtmp;
|
||||
BITVECP nval = LTG_SIGN(newval);
|
||||
int4 i, unionsize=0;
|
||||
BITVECP nval = LTG_SIGN(newval);
|
||||
int4 i,
|
||||
unionsize = 0;
|
||||
|
||||
ALOOPBYTE(
|
||||
valtmp = nval[i] | orig[i];
|
||||
unionsize += SUMBIT(valtmp) - SUMBIT(orig[i]);
|
||||
valtmp = nval[i] | orig[i];
|
||||
unionsize += SUMBIT(valtmp) - SUMBIT(orig[i]);
|
||||
);
|
||||
*penalty = (float)unionsize;
|
||||
*penalty = (float) unionsize;
|
||||
}
|
||||
PG_RETURN_POINTER( penalty );
|
||||
PG_RETURN_POINTER(penalty);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
OffsetNumber pos;
|
||||
int4 cost;
|
||||
typedef struct
|
||||
{
|
||||
OffsetNumber pos;
|
||||
int4 cost;
|
||||
} SPLITCOST;
|
||||
|
||||
static int
|
||||
comparecost( const void *a, const void *b ) {
|
||||
return ((SPLITCOST*)a)->cost - ((SPLITCOST*)b)->cost;
|
||||
comparecost(const void *a, const void *b)
|
||||
{
|
||||
return ((SPLITCOST *) a)->cost - ((SPLITCOST *) b)->cost;
|
||||
}
|
||||
|
||||
Datum
|
||||
_ltree_picksplit(PG_FUNCTION_ARGS) {
|
||||
bytea *entryvec = (bytea*) PG_GETARG_POINTER(0);
|
||||
GIST_SPLITVEC *v = (GIST_SPLITVEC*) PG_GETARG_POINTER(1);
|
||||
OffsetNumber k,j;
|
||||
ltree_gist *datum_l, *datum_r;
|
||||
ABITVEC union_l, union_r;
|
||||
bool firsttime = true;
|
||||
int4 size_alpha,size_beta,sizeu,sizei;
|
||||
int4 size_waste, waste = 0.0;
|
||||
int4 size_l, size_r;
|
||||
int4 nbytes;
|
||||
OffsetNumber seed_1=0, seed_2=0;
|
||||
OffsetNumber *left, *right;
|
||||
Datum
|
||||
_ltree_picksplit(PG_FUNCTION_ARGS)
|
||||
{
|
||||
bytea *entryvec = (bytea *) PG_GETARG_POINTER(0);
|
||||
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
|
||||
OffsetNumber k,
|
||||
j;
|
||||
ltree_gist *datum_l,
|
||||
*datum_r;
|
||||
ABITVEC union_l,
|
||||
union_r;
|
||||
bool firsttime = true;
|
||||
int4 size_alpha,
|
||||
size_beta,
|
||||
sizeu,
|
||||
sizei;
|
||||
int4 size_waste,
|
||||
waste = 0.0;
|
||||
int4 size_l,
|
||||
size_r;
|
||||
int4 nbytes;
|
||||
OffsetNumber seed_1 = 0,
|
||||
seed_2 = 0;
|
||||
OffsetNumber *left,
|
||||
*right;
|
||||
OffsetNumber maxoff;
|
||||
BITVECP ptra, ptrb, ptrc;
|
||||
int i;
|
||||
unsigned char valtmp;
|
||||
SPLITCOST *costvector;
|
||||
ltree_gist *_k, *_j;
|
||||
BITVECP ptra,
|
||||
ptrb,
|
||||
ptrc;
|
||||
int i;
|
||||
unsigned char valtmp;
|
||||
SPLITCOST *costvector;
|
||||
ltree_gist *_k,
|
||||
*_j;
|
||||
|
||||
maxoff = ((VARSIZE(entryvec) - VARHDRSZ) / sizeof(GISTENTRY)) - 2;
|
||||
nbytes = (maxoff + 2) * sizeof(OffsetNumber);
|
||||
v->spl_left = (OffsetNumber *) palloc(nbytes);
|
||||
v->spl_right = (OffsetNumber *) palloc(nbytes);
|
||||
|
||||
for (k = FirstOffsetNumber; k < maxoff; k = OffsetNumberNext(k)) {
|
||||
_k = GETENTRY(entryvec,k);
|
||||
for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j)) {
|
||||
_j = GETENTRY(entryvec,j);
|
||||
if ( LTG_ISALLTRUE(_k) || LTG_ISALLTRUE(_j) ) {
|
||||
for (k = FirstOffsetNumber; k < maxoff; k = OffsetNumberNext(k))
|
||||
{
|
||||
_k = GETENTRY(entryvec, k);
|
||||
for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
|
||||
{
|
||||
_j = GETENTRY(entryvec, j);
|
||||
if (LTG_ISALLTRUE(_k) || LTG_ISALLTRUE(_j))
|
||||
{
|
||||
sizeu = ASIGLENBIT;
|
||||
if ( LTG_ISALLTRUE(_k) && LTG_ISALLTRUE(_j) )
|
||||
if (LTG_ISALLTRUE(_k) && LTG_ISALLTRUE(_j))
|
||||
sizei = ASIGLENBIT;
|
||||
else
|
||||
sizei = ( LTG_ISALLTRUE(_k) ) ?
|
||||
sizebitvec( LTG_SIGN(_j) ) : sizebitvec( LTG_SIGN(_k) );
|
||||
} else {
|
||||
sizei = (LTG_ISALLTRUE(_k)) ?
|
||||
sizebitvec(LTG_SIGN(_j)) : sizebitvec(LTG_SIGN(_k));
|
||||
}
|
||||
else
|
||||
{
|
||||
sizeu = sizei = 0;
|
||||
ptra = LTG_SIGN(_j);
|
||||
ptrb = LTG_SIGN(_k);
|
||||
@@ -278,20 +328,21 @@ _ltree_picksplit(PG_FUNCTION_ARGS) {
|
||||
} while(0)
|
||||
|
||||
ALOOPBYTE(
|
||||
COUNT(0);
|
||||
COUNT(1);
|
||||
COUNT(2);
|
||||
COUNT(3);
|
||||
COUNT(4);
|
||||
COUNT(5);
|
||||
COUNT(6);
|
||||
COUNT(7);
|
||||
ptra = (BITVECP) ( ((char*)ptra) + 1 );
|
||||
ptrb = (BITVECP) ( ((char*)ptrb) + 1 );
|
||||
COUNT(0);
|
||||
COUNT(1);
|
||||
COUNT(2);
|
||||
COUNT(3);
|
||||
COUNT(4);
|
||||
COUNT(5);
|
||||
COUNT(6);
|
||||
COUNT(7);
|
||||
ptra = (BITVECP) (((char *) ptra) + 1);
|
||||
ptrb = (BITVECP) (((char *) ptrb) + 1);
|
||||
);
|
||||
}
|
||||
size_waste = sizeu - sizei;
|
||||
if (size_waste > waste || firsttime) {
|
||||
if (size_waste > waste || firsttime)
|
||||
{
|
||||
waste = size_waste;
|
||||
seed_1 = k;
|
||||
seed_2 = j;
|
||||
@@ -305,129 +356,166 @@ _ltree_picksplit(PG_FUNCTION_ARGS) {
|
||||
right = v->spl_right;
|
||||
v->spl_nright = 0;
|
||||
|
||||
if ( seed_1 == 0 || seed_2 == 0 ) {
|
||||
if (seed_1 == 0 || seed_2 == 0)
|
||||
{
|
||||
seed_1 = 1;
|
||||
seed_2 = 2;
|
||||
}
|
||||
|
||||
/* form initial .. */
|
||||
if ( LTG_ISALLTRUE(GETENTRY(entryvec,seed_1)) ) {
|
||||
datum_l = (ltree_gist*)palloc( LTG_HDRSIZE );
|
||||
datum_l->len = LTG_HDRSIZE; datum_l->flag = LTG_ALLTRUE;
|
||||
if (LTG_ISALLTRUE(GETENTRY(entryvec, seed_1)))
|
||||
{
|
||||
datum_l = (ltree_gist *) palloc(LTG_HDRSIZE);
|
||||
datum_l->len = LTG_HDRSIZE;
|
||||
datum_l->flag = LTG_ALLTRUE;
|
||||
size_l = ASIGLENBIT;
|
||||
} else {
|
||||
datum_l = (ltree_gist*)palloc( LTG_HDRSIZE + ASIGLEN );
|
||||
datum_l->len = LTG_HDRSIZE + ASIGLEN; datum_l->flag = 0;
|
||||
memcpy((void*)LTG_SIGN(datum_l), (void*)LTG_SIGN(GETENTRY(entryvec,seed_1)), sizeof(ABITVEC));
|
||||
size_l = sizebitvec( LTG_SIGN(datum_l) );
|
||||
}
|
||||
if ( LTG_ISALLTRUE(GETENTRY(entryvec,seed_2)) ) {
|
||||
datum_r = (ltree_gist*)palloc( LTG_HDRSIZE );
|
||||
datum_r->len = LTG_HDRSIZE; datum_r->flag = LTG_ALLTRUE;
|
||||
else
|
||||
{
|
||||
datum_l = (ltree_gist *) palloc(LTG_HDRSIZE + ASIGLEN);
|
||||
datum_l->len = LTG_HDRSIZE + ASIGLEN;
|
||||
datum_l->flag = 0;
|
||||
memcpy((void *) LTG_SIGN(datum_l), (void *) LTG_SIGN(GETENTRY(entryvec, seed_1)), sizeof(ABITVEC));
|
||||
size_l = sizebitvec(LTG_SIGN(datum_l));
|
||||
}
|
||||
if (LTG_ISALLTRUE(GETENTRY(entryvec, seed_2)))
|
||||
{
|
||||
datum_r = (ltree_gist *) palloc(LTG_HDRSIZE);
|
||||
datum_r->len = LTG_HDRSIZE;
|
||||
datum_r->flag = LTG_ALLTRUE;
|
||||
size_r = ASIGLENBIT;
|
||||
} else {
|
||||
datum_r = (ltree_gist*)palloc( LTG_HDRSIZE + ASIGLEN );
|
||||
datum_r->len = LTG_HDRSIZE + ASIGLEN; datum_r->flag = 0;
|
||||
memcpy((void*)LTG_SIGN(datum_r), (void*)LTG_SIGN(GETENTRY(entryvec,seed_2)), sizeof(ABITVEC));
|
||||
size_r = sizebitvec( LTG_SIGN(datum_r) );
|
||||
}
|
||||
else
|
||||
{
|
||||
datum_r = (ltree_gist *) palloc(LTG_HDRSIZE + ASIGLEN);
|
||||
datum_r->len = LTG_HDRSIZE + ASIGLEN;
|
||||
datum_r->flag = 0;
|
||||
memcpy((void *) LTG_SIGN(datum_r), (void *) LTG_SIGN(GETENTRY(entryvec, seed_2)), sizeof(ABITVEC));
|
||||
size_r = sizebitvec(LTG_SIGN(datum_r));
|
||||
}
|
||||
|
||||
maxoff = OffsetNumberNext(maxoff);
|
||||
/* sort before ... */
|
||||
costvector=(SPLITCOST*)palloc( sizeof(SPLITCOST)*maxoff );
|
||||
for (j = FirstOffsetNumber; j <= maxoff; j = OffsetNumberNext(j)) {
|
||||
costvector[j-1].pos = j;
|
||||
_j = GETENTRY(entryvec,j);
|
||||
if ( LTG_ISALLTRUE(_j) ) {
|
||||
costvector = (SPLITCOST *) palloc(sizeof(SPLITCOST) * maxoff);
|
||||
for (j = FirstOffsetNumber; j <= maxoff; j = OffsetNumberNext(j))
|
||||
{
|
||||
costvector[j - 1].pos = j;
|
||||
_j = GETENTRY(entryvec, j);
|
||||
if (LTG_ISALLTRUE(_j))
|
||||
{
|
||||
size_alpha = ASIGLENBIT - size_l;
|
||||
size_beta = ASIGLENBIT - size_r;
|
||||
} else {
|
||||
ptra = LTG_SIGN( datum_l );
|
||||
ptrb = LTG_SIGN( datum_r );
|
||||
ptrc = LTG_SIGN( _j );
|
||||
size_beta = ASIGLENBIT - size_r;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptra = LTG_SIGN(datum_l);
|
||||
ptrb = LTG_SIGN(datum_r);
|
||||
ptrc = LTG_SIGN(_j);
|
||||
size_beta = size_alpha = 0;
|
||||
if ( LTG_ISALLTRUE(datum_l) ) {
|
||||
if ( !LTG_ISALLTRUE(datum_r) ) {
|
||||
if (LTG_ISALLTRUE(datum_l))
|
||||
{
|
||||
if (!LTG_ISALLTRUE(datum_r))
|
||||
{
|
||||
ALOOPBIT(
|
||||
if ( GETBIT(ptrc,i) && ! GETBIT(ptrb,i) )
|
||||
size_beta++;
|
||||
if (GETBIT(ptrc, i) && !GETBIT(ptrb, i))
|
||||
size_beta++;
|
||||
);
|
||||
}
|
||||
} else if ( LTG_ISALLTRUE(datum_r) ) {
|
||||
if ( !LTG_ISALLTRUE(datum_l) ) {
|
||||
}
|
||||
else if (LTG_ISALLTRUE(datum_r))
|
||||
{
|
||||
if (!LTG_ISALLTRUE(datum_l))
|
||||
{
|
||||
ALOOPBIT(
|
||||
if ( GETBIT(ptrc,i) && ! GETBIT(ptra,i) )
|
||||
size_alpha++;
|
||||
if (GETBIT(ptrc, i) && !GETBIT(ptra, i))
|
||||
size_alpha++;
|
||||
);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ALOOPBIT(
|
||||
if ( GETBIT(ptrc,i) && ! GETBIT(ptra,i) )
|
||||
size_alpha++;
|
||||
if ( GETBIT(ptrc,i) && ! GETBIT(ptrb,i) )
|
||||
size_beta++;
|
||||
if (GETBIT(ptrc, i) && !GETBIT(ptra, i))
|
||||
size_alpha++;
|
||||
if (GETBIT(ptrc, i) && !GETBIT(ptrb, i))
|
||||
size_beta++;
|
||||
);
|
||||
}
|
||||
}
|
||||
costvector[j-1].cost = abs( size_alpha - size_beta );
|
||||
costvector[j - 1].cost = abs(size_alpha - size_beta);
|
||||
}
|
||||
qsort( (void*)costvector, maxoff, sizeof(SPLITCOST), comparecost );
|
||||
qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost);
|
||||
|
||||
for (k = 0; k < maxoff; k++) {
|
||||
for (k = 0; k < maxoff; k++)
|
||||
{
|
||||
j = costvector[k].pos;
|
||||
_j = GETENTRY(entryvec,j);
|
||||
if ( j == seed_1 ) {
|
||||
_j = GETENTRY(entryvec, j);
|
||||
if (j == seed_1)
|
||||
{
|
||||
*left++ = j;
|
||||
v->spl_nleft++;
|
||||
continue;
|
||||
} else if ( j == seed_2 ) {
|
||||
}
|
||||
else if (j == seed_2)
|
||||
{
|
||||
*right++ = j;
|
||||
v->spl_nright++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( LTG_ISALLTRUE(datum_l) || LTG_ISALLTRUE(_j) ) {
|
||||
if (LTG_ISALLTRUE(datum_l) || LTG_ISALLTRUE(_j))
|
||||
size_alpha = ASIGLENBIT;
|
||||
} else {
|
||||
else
|
||||
{
|
||||
ptra = LTG_SIGN(_j);
|
||||
ptrb = LTG_SIGN(datum_l);
|
||||
size_alpha = 0;
|
||||
ALOOPBYTE(
|
||||
valtmp = union_l[i] = ptra[i] | ptrb[i];
|
||||
size_alpha += SUMBIT( valtmp );
|
||||
valtmp = union_l[i] = ptra[i] | ptrb[i];
|
||||
size_alpha += SUMBIT(valtmp);
|
||||
);
|
||||
}
|
||||
|
||||
if ( LTG_ISALLTRUE(datum_r) || LTG_ISALLTRUE(_j) ) {
|
||||
if (LTG_ISALLTRUE(datum_r) || LTG_ISALLTRUE(_j))
|
||||
size_beta = ASIGLENBIT;
|
||||
} else {
|
||||
else
|
||||
{
|
||||
ptra = LTG_SIGN(_j);
|
||||
ptrb = LTG_SIGN(datum_r);
|
||||
size_beta = 0;
|
||||
ALOOPBYTE(
|
||||
valtmp = union_r[i] = ptra[i] | ptrb[i];
|
||||
size_beta += SUMBIT( valtmp );
|
||||
valtmp = union_r[i] = ptra[i] | ptrb[i];
|
||||
size_beta += SUMBIT(valtmp);
|
||||
);
|
||||
}
|
||||
|
||||
if (size_alpha - size_l < size_beta - size_r + WISH_F(v->spl_nleft, v->spl_nright, 0.1)) {
|
||||
if ( ! LTG_ISALLTRUE( datum_l ) ) {
|
||||
if ( size_alpha == ASIGLENBIT ) {
|
||||
if ( size_alpha != size_l )
|
||||
MemSet( (void*)LTG_SIGN(datum_l),0xff, sizeof(ABITVEC));
|
||||
} else
|
||||
memcpy( (void*)LTG_SIGN(datum_l), (void*)union_l, sizeof(ABITVEC) );
|
||||
if (size_alpha - size_l < size_beta - size_r + WISH_F(v->spl_nleft, v->spl_nright, 0.1))
|
||||
{
|
||||
if (!LTG_ISALLTRUE(datum_l))
|
||||
{
|
||||
if (size_alpha == ASIGLENBIT)
|
||||
{
|
||||
if (size_alpha != size_l)
|
||||
MemSet((void *) LTG_SIGN(datum_l), 0xff, sizeof(ABITVEC));
|
||||
}
|
||||
else
|
||||
memcpy((void *) LTG_SIGN(datum_l), (void *) union_l, sizeof(ABITVEC));
|
||||
}
|
||||
size_l = size_alpha;
|
||||
*left++ = j;
|
||||
v->spl_nleft++;
|
||||
} else {
|
||||
if ( ! LTG_ISALLTRUE( datum_r ) ) {
|
||||
if ( size_beta == ASIGLENBIT ) {
|
||||
if ( size_beta != size_r )
|
||||
MemSet( (void*)LTG_SIGN(datum_r),0xff, sizeof(ABITVEC));
|
||||
} else
|
||||
memcpy( (void*)LTG_SIGN(datum_r), (void*)union_r, sizeof(ABITVEC) );
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!LTG_ISALLTRUE(datum_r))
|
||||
{
|
||||
if (size_beta == ASIGLENBIT)
|
||||
{
|
||||
if (size_beta != size_r)
|
||||
MemSet((void *) LTG_SIGN(datum_r), 0xff, sizeof(ABITVEC));
|
||||
}
|
||||
else
|
||||
memcpy((void *) LTG_SIGN(datum_r), (void *) union_r, sizeof(ABITVEC));
|
||||
}
|
||||
size_r = size_beta;
|
||||
*right++ = j;
|
||||
@@ -441,23 +529,25 @@ _ltree_picksplit(PG_FUNCTION_ARGS) {
|
||||
v->spl_ldatum = PointerGetDatum(datum_l);
|
||||
v->spl_rdatum = PointerGetDatum(datum_r);
|
||||
|
||||
PG_RETURN_POINTER( v );
|
||||
PG_RETURN_POINTER(v);
|
||||
}
|
||||
|
||||
static bool
|
||||
gist_te(ltree_gist *key, ltree* query) {
|
||||
ltree_level *curq = LTREE_FIRST(query);
|
||||
BITVECP sign = LTG_SIGN(key);
|
||||
int qlen = query->numlevel;
|
||||
gist_te(ltree_gist * key, ltree * query)
|
||||
{
|
||||
ltree_level *curq = LTREE_FIRST(query);
|
||||
BITVECP sign = LTG_SIGN(key);
|
||||
int qlen = query->numlevel;
|
||||
unsigned int hv;
|
||||
|
||||
if ( LTG_ISALLTRUE(key) )
|
||||
if (LTG_ISALLTRUE(key))
|
||||
return true;
|
||||
|
||||
while( qlen>0 ) {
|
||||
hv = ltree_crc32_sz(curq->name,curq->len);
|
||||
if ( ! GETBIT( sign, AHASHVAL(hv) ) )
|
||||
return false;
|
||||
while (qlen > 0)
|
||||
{
|
||||
hv = ltree_crc32_sz(curq->name, curq->len);
|
||||
if (!GETBIT(sign, AHASHVAL(hv)))
|
||||
return false;
|
||||
curq = LEVEL_NEXT(curq);
|
||||
qlen--;
|
||||
}
|
||||
@@ -466,48 +556,56 @@ gist_te(ltree_gist *key, ltree* query) {
|
||||
}
|
||||
|
||||
static bool
|
||||
checkcondition_bit(void *checkval, ITEM* val ) {
|
||||
return ( FLG_CANLOOKSIGN(val->flag) ) ? GETBIT( checkval, AHASHVAL( val->val ) ) : true;
|
||||
checkcondition_bit(void *checkval, ITEM * val)
|
||||
{
|
||||
return (FLG_CANLOOKSIGN(val->flag)) ? GETBIT(checkval, AHASHVAL(val->val)) : true;
|
||||
}
|
||||
|
||||
static bool
|
||||
gist_qtxt(ltree_gist *key, ltxtquery* query) {
|
||||
if ( LTG_ISALLTRUE(key) )
|
||||
gist_qtxt(ltree_gist * key, ltxtquery * query)
|
||||
{
|
||||
if (LTG_ISALLTRUE(key))
|
||||
return true;
|
||||
|
||||
|
||||
return ltree_execute(
|
||||
GETQUERY(query),
|
||||
(void*)LTG_SIGN(key), false,
|
||||
checkcondition_bit
|
||||
);
|
||||
GETQUERY(query),
|
||||
(void *) LTG_SIGN(key), false,
|
||||
checkcondition_bit
|
||||
);
|
||||
}
|
||||
|
||||
static bool
|
||||
gist_qe(ltree_gist *key, lquery* query) {
|
||||
lquery_level *curq = LQUERY_FIRST(query);
|
||||
BITVECP sign = LTG_SIGN(key);
|
||||
int qlen = query->numlevel;
|
||||
|
||||
if ( LTG_ISALLTRUE(key) )
|
||||
gist_qe(ltree_gist * key, lquery * query)
|
||||
{
|
||||
lquery_level *curq = LQUERY_FIRST(query);
|
||||
BITVECP sign = LTG_SIGN(key);
|
||||
int qlen = query->numlevel;
|
||||
|
||||
if (LTG_ISALLTRUE(key))
|
||||
return true;
|
||||
|
||||
while( qlen>0 ) {
|
||||
if ( curq->numvar && LQL_CANLOOKSIGN(curq) ) {
|
||||
bool isexist=false;
|
||||
int vlen = curq->numvar;
|
||||
|
||||
while (qlen > 0)
|
||||
{
|
||||
if (curq->numvar && LQL_CANLOOKSIGN(curq))
|
||||
{
|
||||
bool isexist = false;
|
||||
int vlen = curq->numvar;
|
||||
lquery_variant *curv = LQL_FIRST(curq);
|
||||
while( vlen>0 ) {
|
||||
if ( GETBIT( sign, AHASHVAL( curv->val ) ) ) {
|
||||
isexist=true;
|
||||
|
||||
while (vlen > 0)
|
||||
{
|
||||
if (GETBIT(sign, AHASHVAL(curv->val)))
|
||||
{
|
||||
isexist = true;
|
||||
break;
|
||||
}
|
||||
curv = LVAR_NEXT(curv);
|
||||
vlen--;
|
||||
}
|
||||
if ( !isexist )
|
||||
if (!isexist)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
curq = LQL_NEXT(curq);
|
||||
qlen--;
|
||||
}
|
||||
@@ -516,34 +614,35 @@ gist_qe(ltree_gist *key, lquery* query) {
|
||||
}
|
||||
|
||||
|
||||
Datum
|
||||
_ltree_consistent(PG_FUNCTION_ARGS) {
|
||||
GISTENTRY *entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
char *query = (char*)DatumGetPointer( PG_DETOAST_DATUM(PG_GETARG_DATUM(1)) );
|
||||
ltree_gist *key = (ltree_gist*)DatumGetPointer( entry->key );
|
||||
Datum
|
||||
_ltree_consistent(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
|
||||
char *query = (char *) DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(1)));
|
||||
ltree_gist *key = (ltree_gist *) DatumGetPointer(entry->key);
|
||||
StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
|
||||
bool res = false;
|
||||
bool res = false;
|
||||
|
||||
#ifndef assert_enabled
|
||||
#ifndef assert_enabled
|
||||
#define assert_enabled 0
|
||||
#endif
|
||||
|
||||
switch( strategy ) {
|
||||
|
||||
switch (strategy)
|
||||
{
|
||||
case 10:
|
||||
case 11:
|
||||
res = gist_te(key, (ltree*)query);
|
||||
res = gist_te(key, (ltree *) query);
|
||||
break;
|
||||
case 12:
|
||||
case 13:
|
||||
res = gist_qe(key, (lquery*)query);
|
||||
break;
|
||||
res = gist_qe(key, (lquery *) query);
|
||||
break;
|
||||
case 14:
|
||||
case 15:
|
||||
res = gist_qtxt(key, (ltxtquery*)query);
|
||||
break;
|
||||
res = gist_qtxt(key, (ltxtquery *) query);
|
||||
break;
|
||||
default:
|
||||
elog(ERROR,"Unknown StrategyNumber: %d", strategy);
|
||||
elog(ERROR, "Unknown StrategyNumber: %d", strategy);
|
||||
}
|
||||
PG_RETURN_BOOL(res);
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* op function for ltree[]
|
||||
* op function for ltree[]
|
||||
* Teodor Sigaev <teodor@stack.net>
|
||||
*/
|
||||
|
||||
@@ -16,223 +16,253 @@ PG_FUNCTION_INFO_V1(_ltq_rregex);
|
||||
PG_FUNCTION_INFO_V1(_ltxtq_exec);
|
||||
PG_FUNCTION_INFO_V1(_ltxtq_rexec);
|
||||
|
||||
Datum _ltree_r_isparent(PG_FUNCTION_ARGS);
|
||||
Datum _ltree_r_risparent(PG_FUNCTION_ARGS);
|
||||
Datum _ltree_r_isparent(PG_FUNCTION_ARGS);
|
||||
Datum _ltree_r_risparent(PG_FUNCTION_ARGS);
|
||||
|
||||
PG_FUNCTION_INFO_V1(_ltree_extract_isparent);
|
||||
PG_FUNCTION_INFO_V1(_ltree_extract_risparent);
|
||||
PG_FUNCTION_INFO_V1(_ltq_extract_regex);
|
||||
PG_FUNCTION_INFO_V1(_ltxtq_extract_exec);
|
||||
Datum _ltree_extract_isparent(PG_FUNCTION_ARGS);
|
||||
Datum _ltree_extract_risparent(PG_FUNCTION_ARGS);
|
||||
Datum _ltq_extract_regex(PG_FUNCTION_ARGS);
|
||||
Datum _ltxtq_extract_exec(PG_FUNCTION_ARGS);
|
||||
Datum _ltree_extract_isparent(PG_FUNCTION_ARGS);
|
||||
Datum _ltree_extract_risparent(PG_FUNCTION_ARGS);
|
||||
Datum _ltq_extract_regex(PG_FUNCTION_ARGS);
|
||||
Datum _ltxtq_extract_exec(PG_FUNCTION_ARGS);
|
||||
|
||||
PG_FUNCTION_INFO_V1(_lca);
|
||||
Datum _lca(PG_FUNCTION_ARGS);
|
||||
Datum _lca(PG_FUNCTION_ARGS);
|
||||
|
||||
typedef Datum (*PGCALL2) (PG_FUNCTION_ARGS);
|
||||
|
||||
typedef Datum (*PGCALL2)(PG_FUNCTION_ARGS);
|
||||
#define NEXTVAL(x) ( (ltree*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
|
||||
|
||||
static bool
|
||||
array_iterator( ArrayType *la, PGCALL2 callback, void* param, ltree ** found) {
|
||||
int num=ArrayGetNItems( ARR_NDIM(la), ARR_DIMS(la));
|
||||
ltree *item = (ltree*)ARR_DATA_PTR(la);
|
||||
array_iterator(ArrayType *la, PGCALL2 callback, void *param, ltree ** found)
|
||||
{
|
||||
int num = ArrayGetNItems(ARR_NDIM(la), ARR_DIMS(la));
|
||||
ltree *item = (ltree *) ARR_DATA_PTR(la);
|
||||
|
||||
if ( ARR_NDIM(la) !=1 )
|
||||
elog(ERROR,"Dimension of array != 1");
|
||||
if (ARR_NDIM(la) != 1)
|
||||
elog(ERROR, "Dimension of array != 1");
|
||||
|
||||
if ( found )
|
||||
*found=NULL;
|
||||
while( num>0 ) {
|
||||
if ( DatumGetBool( DirectFunctionCall2( callback,
|
||||
PointerGetDatum(item), PointerGetDatum(param) ) ) ) {
|
||||
if (found)
|
||||
*found = NULL;
|
||||
while (num > 0)
|
||||
{
|
||||
if (DatumGetBool(DirectFunctionCall2(callback,
|
||||
PointerGetDatum(item), PointerGetDatum(param))))
|
||||
{
|
||||
|
||||
if ( found )
|
||||
if (found)
|
||||
*found = item;
|
||||
return true;
|
||||
}
|
||||
num--;
|
||||
item = NEXTVAL(item);
|
||||
item = NEXTVAL(item);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Datum
|
||||
_ltree_isparent(PG_FUNCTION_ARGS) {
|
||||
ArrayType *la = PG_GETARG_ARRAYTYPE_P(0);
|
||||
ltree *query = PG_GETARG_LTREE(1);
|
||||
bool res = array_iterator( la, ltree_isparent, (void*)query, NULL );
|
||||
PG_FREE_IF_COPY(la,0);
|
||||
PG_FREE_IF_COPY(query,1);
|
||||
_ltree_isparent(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayType *la = PG_GETARG_ARRAYTYPE_P(0);
|
||||
ltree *query = PG_GETARG_LTREE(1);
|
||||
bool res = array_iterator(la, ltree_isparent, (void *) query, NULL);
|
||||
|
||||
PG_FREE_IF_COPY(la, 0);
|
||||
PG_FREE_IF_COPY(query, 1);
|
||||
PG_RETURN_BOOL(res);
|
||||
}
|
||||
|
||||
Datum
|
||||
_ltree_r_isparent(PG_FUNCTION_ARGS) {
|
||||
PG_RETURN_DATUM( DirectFunctionCall2( _ltree_isparent,
|
||||
PG_GETARG_DATUM(1),
|
||||
PG_GETARG_DATUM(0)
|
||||
) );
|
||||
_ltree_r_isparent(PG_FUNCTION_ARGS)
|
||||
{
|
||||
PG_RETURN_DATUM(DirectFunctionCall2(_ltree_isparent,
|
||||
PG_GETARG_DATUM(1),
|
||||
PG_GETARG_DATUM(0)
|
||||
));
|
||||
}
|
||||
|
||||
Datum
|
||||
_ltree_risparent(PG_FUNCTION_ARGS) {
|
||||
ArrayType *la = PG_GETARG_ARRAYTYPE_P(0);
|
||||
ltree *query = PG_GETARG_LTREE(1);
|
||||
bool res = array_iterator( la, ltree_risparent, (void*)query, NULL );
|
||||
PG_FREE_IF_COPY(la,0);
|
||||
PG_FREE_IF_COPY(query,1);
|
||||
_ltree_risparent(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayType *la = PG_GETARG_ARRAYTYPE_P(0);
|
||||
ltree *query = PG_GETARG_LTREE(1);
|
||||
bool res = array_iterator(la, ltree_risparent, (void *) query, NULL);
|
||||
|
||||
PG_FREE_IF_COPY(la, 0);
|
||||
PG_FREE_IF_COPY(query, 1);
|
||||
PG_RETURN_BOOL(res);
|
||||
}
|
||||
|
||||
Datum
|
||||
_ltree_r_risparent(PG_FUNCTION_ARGS) {
|
||||
PG_RETURN_DATUM( DirectFunctionCall2( _ltree_risparent,
|
||||
PG_GETARG_DATUM(1),
|
||||
PG_GETARG_DATUM(0)
|
||||
) );
|
||||
_ltree_r_risparent(PG_FUNCTION_ARGS)
|
||||
{
|
||||
PG_RETURN_DATUM(DirectFunctionCall2(_ltree_risparent,
|
||||
PG_GETARG_DATUM(1),
|
||||
PG_GETARG_DATUM(0)
|
||||
));
|
||||
}
|
||||
|
||||
Datum
|
||||
_ltq_regex(PG_FUNCTION_ARGS) {
|
||||
ArrayType *la = PG_GETARG_ARRAYTYPE_P(0);
|
||||
lquery *query = PG_GETARG_LQUERY(1);
|
||||
bool res = array_iterator( la, ltq_regex, (void*)query, NULL );
|
||||
PG_FREE_IF_COPY(la,0);
|
||||
PG_FREE_IF_COPY(query,1);
|
||||
_ltq_regex(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayType *la = PG_GETARG_ARRAYTYPE_P(0);
|
||||
lquery *query = PG_GETARG_LQUERY(1);
|
||||
bool res = array_iterator(la, ltq_regex, (void *) query, NULL);
|
||||
|
||||
PG_FREE_IF_COPY(la, 0);
|
||||
PG_FREE_IF_COPY(query, 1);
|
||||
PG_RETURN_BOOL(res);
|
||||
}
|
||||
|
||||
Datum
|
||||
_ltq_rregex(PG_FUNCTION_ARGS) {
|
||||
PG_RETURN_DATUM( DirectFunctionCall2( _ltq_regex,
|
||||
PG_GETARG_DATUM(1),
|
||||
PG_GETARG_DATUM(0)
|
||||
) );
|
||||
_ltq_rregex(PG_FUNCTION_ARGS)
|
||||
{
|
||||
PG_RETURN_DATUM(DirectFunctionCall2(_ltq_regex,
|
||||
PG_GETARG_DATUM(1),
|
||||
PG_GETARG_DATUM(0)
|
||||
));
|
||||
}
|
||||
|
||||
Datum
|
||||
_ltxtq_exec(PG_FUNCTION_ARGS) {
|
||||
ArrayType *la = PG_GETARG_ARRAYTYPE_P(0);
|
||||
ltxtquery *query = PG_GETARG_LTXTQUERY(1);
|
||||
bool res = array_iterator( la, ltxtq_exec, (void*)query, NULL );
|
||||
PG_FREE_IF_COPY(la,0);
|
||||
PG_FREE_IF_COPY(query,1);
|
||||
Datum
|
||||
_ltxtq_exec(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayType *la = PG_GETARG_ARRAYTYPE_P(0);
|
||||
ltxtquery *query = PG_GETARG_LTXTQUERY(1);
|
||||
bool res = array_iterator(la, ltxtq_exec, (void *) query, NULL);
|
||||
|
||||
PG_FREE_IF_COPY(la, 0);
|
||||
PG_FREE_IF_COPY(query, 1);
|
||||
PG_RETURN_BOOL(res);
|
||||
}
|
||||
|
||||
Datum
|
||||
_ltxtq_rexec(PG_FUNCTION_ARGS) {
|
||||
PG_RETURN_DATUM( DirectFunctionCall2( _ltxtq_exec,
|
||||
PG_GETARG_DATUM(1),
|
||||
PG_GETARG_DATUM(0)
|
||||
) );
|
||||
_ltxtq_rexec(PG_FUNCTION_ARGS)
|
||||
{
|
||||
PG_RETURN_DATUM(DirectFunctionCall2(_ltxtq_exec,
|
||||
PG_GETARG_DATUM(1),
|
||||
PG_GETARG_DATUM(0)
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
Datum
|
||||
_ltree_extract_isparent(PG_FUNCTION_ARGS) {
|
||||
ArrayType *la = PG_GETARG_ARRAYTYPE_P(0);
|
||||
ltree *query = PG_GETARG_LTREE(1);
|
||||
ltree *found,*item;
|
||||
Datum
|
||||
_ltree_extract_isparent(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayType *la = PG_GETARG_ARRAYTYPE_P(0);
|
||||
ltree *query = PG_GETARG_LTREE(1);
|
||||
ltree *found,
|
||||
*item;
|
||||
|
||||
if ( !array_iterator( la, ltree_isparent, (void*)query, &found ) ) {
|
||||
PG_FREE_IF_COPY(la,0);
|
||||
PG_FREE_IF_COPY(query,1);
|
||||
if (!array_iterator(la, ltree_isparent, (void *) query, &found))
|
||||
{
|
||||
PG_FREE_IF_COPY(la, 0);
|
||||
PG_FREE_IF_COPY(query, 1);
|
||||
PG_RETURN_NULL();
|
||||
}
|
||||
|
||||
item = (ltree*)palloc( found->len );
|
||||
memcpy( item, found, found->len );
|
||||
|
||||
PG_FREE_IF_COPY(la,0);
|
||||
PG_FREE_IF_COPY(query,1);
|
||||
PG_RETURN_POINTER(item);
|
||||
}
|
||||
item = (ltree *) palloc(found->len);
|
||||
memcpy(item, found, found->len);
|
||||
|
||||
Datum
|
||||
_ltree_extract_risparent(PG_FUNCTION_ARGS) {
|
||||
ArrayType *la = PG_GETARG_ARRAYTYPE_P(0);
|
||||
ltree *query = PG_GETARG_LTREE(1);
|
||||
ltree *found,*item;
|
||||
|
||||
if ( !array_iterator( la, ltree_risparent, (void*)query, &found ) ) {
|
||||
PG_FREE_IF_COPY(la,0);
|
||||
PG_FREE_IF_COPY(query,1);
|
||||
PG_RETURN_NULL();
|
||||
}
|
||||
|
||||
item = (ltree*)palloc( found->len );
|
||||
memcpy( item, found, found->len );
|
||||
|
||||
PG_FREE_IF_COPY(la,0);
|
||||
PG_FREE_IF_COPY(query,1);
|
||||
PG_RETURN_POINTER(item);
|
||||
}
|
||||
|
||||
Datum
|
||||
_ltq_extract_regex(PG_FUNCTION_ARGS) {
|
||||
ArrayType *la = PG_GETARG_ARRAYTYPE_P(0);
|
||||
lquery *query = PG_GETARG_LQUERY(1);
|
||||
ltree *found,*item;
|
||||
|
||||
if ( !array_iterator( la, ltq_regex, (void*)query, &found ) ) {
|
||||
PG_FREE_IF_COPY(la,0);
|
||||
PG_FREE_IF_COPY(query,1);
|
||||
PG_RETURN_NULL();
|
||||
}
|
||||
|
||||
item = (ltree*)palloc( found->len );
|
||||
memcpy( item, found, found->len );
|
||||
|
||||
PG_FREE_IF_COPY(la,0);
|
||||
PG_FREE_IF_COPY(query,1);
|
||||
PG_RETURN_POINTER(item);
|
||||
}
|
||||
|
||||
Datum
|
||||
_ltxtq_extract_exec(PG_FUNCTION_ARGS) {
|
||||
ArrayType *la = PG_GETARG_ARRAYTYPE_P(0);
|
||||
ltxtquery *query = PG_GETARG_LTXTQUERY(1);
|
||||
ltree *found,*item;
|
||||
|
||||
if ( !array_iterator( la, ltxtq_exec, (void*)query, &found ) ) {
|
||||
PG_FREE_IF_COPY(la,0);
|
||||
PG_FREE_IF_COPY(query,1);
|
||||
PG_RETURN_NULL();
|
||||
}
|
||||
|
||||
item = (ltree*)palloc( found->len );
|
||||
memcpy( item, found, found->len );
|
||||
|
||||
PG_FREE_IF_COPY(la,0);
|
||||
PG_FREE_IF_COPY(query,1);
|
||||
PG_FREE_IF_COPY(la, 0);
|
||||
PG_FREE_IF_COPY(query, 1);
|
||||
PG_RETURN_POINTER(item);
|
||||
}
|
||||
|
||||
Datum
|
||||
_lca(PG_FUNCTION_ARGS) {
|
||||
ArrayType *la = PG_GETARG_ARRAYTYPE_P(0);
|
||||
int num=ArrayGetNItems( ARR_NDIM(la), ARR_DIMS(la));
|
||||
ltree *item = (ltree*)ARR_DATA_PTR(la);
|
||||
ltree **a,*res;
|
||||
_ltree_extract_risparent(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayType *la = PG_GETARG_ARRAYTYPE_P(0);
|
||||
ltree *query = PG_GETARG_LTREE(1);
|
||||
ltree *found,
|
||||
*item;
|
||||
|
||||
a=(ltree**)palloc( sizeof(ltree*) * num );
|
||||
while( num>0 ) {
|
||||
if (!array_iterator(la, ltree_risparent, (void *) query, &found))
|
||||
{
|
||||
PG_FREE_IF_COPY(la, 0);
|
||||
PG_FREE_IF_COPY(query, 1);
|
||||
PG_RETURN_NULL();
|
||||
}
|
||||
|
||||
item = (ltree *) palloc(found->len);
|
||||
memcpy(item, found, found->len);
|
||||
|
||||
PG_FREE_IF_COPY(la, 0);
|
||||
PG_FREE_IF_COPY(query, 1);
|
||||
PG_RETURN_POINTER(item);
|
||||
}
|
||||
|
||||
Datum
|
||||
_ltq_extract_regex(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayType *la = PG_GETARG_ARRAYTYPE_P(0);
|
||||
lquery *query = PG_GETARG_LQUERY(1);
|
||||
ltree *found,
|
||||
*item;
|
||||
|
||||
if (!array_iterator(la, ltq_regex, (void *) query, &found))
|
||||
{
|
||||
PG_FREE_IF_COPY(la, 0);
|
||||
PG_FREE_IF_COPY(query, 1);
|
||||
PG_RETURN_NULL();
|
||||
}
|
||||
|
||||
item = (ltree *) palloc(found->len);
|
||||
memcpy(item, found, found->len);
|
||||
|
||||
PG_FREE_IF_COPY(la, 0);
|
||||
PG_FREE_IF_COPY(query, 1);
|
||||
PG_RETURN_POINTER(item);
|
||||
}
|
||||
|
||||
Datum
|
||||
_ltxtq_extract_exec(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayType *la = PG_GETARG_ARRAYTYPE_P(0);
|
||||
ltxtquery *query = PG_GETARG_LTXTQUERY(1);
|
||||
ltree *found,
|
||||
*item;
|
||||
|
||||
if (!array_iterator(la, ltxtq_exec, (void *) query, &found))
|
||||
{
|
||||
PG_FREE_IF_COPY(la, 0);
|
||||
PG_FREE_IF_COPY(query, 1);
|
||||
PG_RETURN_NULL();
|
||||
}
|
||||
|
||||
item = (ltree *) palloc(found->len);
|
||||
memcpy(item, found, found->len);
|
||||
|
||||
PG_FREE_IF_COPY(la, 0);
|
||||
PG_FREE_IF_COPY(query, 1);
|
||||
PG_RETURN_POINTER(item);
|
||||
}
|
||||
|
||||
Datum
|
||||
_lca(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ArrayType *la = PG_GETARG_ARRAYTYPE_P(0);
|
||||
int num = ArrayGetNItems(ARR_NDIM(la), ARR_DIMS(la));
|
||||
ltree *item = (ltree *) ARR_DATA_PTR(la);
|
||||
ltree **a,
|
||||
*res;
|
||||
|
||||
a = (ltree **) palloc(sizeof(ltree *) * num);
|
||||
while (num > 0)
|
||||
{
|
||||
num--;
|
||||
a[num] = item;
|
||||
item = NEXTVAL(item);
|
||||
}
|
||||
res = lca_inner(a, ArrayGetNItems( ARR_NDIM(la), ARR_DIMS(la)));
|
||||
res = lca_inner(a, ArrayGetNItems(ARR_NDIM(la), ARR_DIMS(la)));
|
||||
pfree(a);
|
||||
|
||||
PG_FREE_IF_COPY(la,0);
|
||||
PG_FREE_IF_COPY(la, 0);
|
||||
|
||||
if ( res )
|
||||
PG_RETURN_POINTER(res);
|
||||
else
|
||||
PG_RETURN_NULL();
|
||||
if (res)
|
||||
PG_RETURN_POINTER(res);
|
||||
else
|
||||
PG_RETURN_NULL();
|
||||
}
|
||||
|
||||
|
@@ -105,6 +105,6 @@ ltree_crc32_sz(char *buf, int size)
|
||||
len = 0;
|
||||
nr = size;
|
||||
for (len += nr, p = buf; nr--; ++p)
|
||||
_CRC32_(crc, TOLOWER((unsigned int)*p));
|
||||
_CRC32_(crc, TOLOWER((unsigned int) *p));
|
||||
return ~crc;
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* op function for ltree and lquery
|
||||
* op function for ltree and lquery
|
||||
* Teodor Sigaev <teodor@stack.net>
|
||||
*/
|
||||
|
||||
@@ -9,27 +9,29 @@
|
||||
PG_FUNCTION_INFO_V1(ltq_regex);
|
||||
PG_FUNCTION_INFO_V1(ltq_rregex);
|
||||
|
||||
typedef struct {
|
||||
lquery_level *q;
|
||||
int nq;
|
||||
ltree_level *t;
|
||||
int nt;
|
||||
int posq;
|
||||
int post;
|
||||
} FieldNot;
|
||||
typedef struct
|
||||
{
|
||||
lquery_level *q;
|
||||
int nq;
|
||||
ltree_level *t;
|
||||
int nt;
|
||||
int posq;
|
||||
int post;
|
||||
} FieldNot;
|
||||
|
||||
static char *
|
||||
getlexem(char *start, char *end, int *len) {
|
||||
char *ptr;
|
||||
|
||||
while( start<end && *start == '_' )
|
||||
getlexem(char *start, char *end, int *len)
|
||||
{
|
||||
char *ptr;
|
||||
|
||||
while (start < end && *start == '_')
|
||||
start++;
|
||||
|
||||
ptr = start;
|
||||
if ( ptr == end )
|
||||
if (ptr == end)
|
||||
return NULL;
|
||||
|
||||
while( ptr < end && *ptr != '_')
|
||||
while (ptr < end && *ptr != '_')
|
||||
ptr++;
|
||||
|
||||
*len = ptr - start;
|
||||
@@ -37,31 +39,36 @@ getlexem(char *start, char *end, int *len) {
|
||||
}
|
||||
|
||||
bool
|
||||
compare_subnode( ltree_level *t, char *qn, int len, int (*cmpptr)(const char *,const char *,size_t), bool anyend ) {
|
||||
char *endt = t->name + t->len;
|
||||
char *endq = qn + len;
|
||||
char *tn;
|
||||
int lent,lenq;
|
||||
bool isok;
|
||||
compare_subnode(ltree_level * t, char *qn, int len, int (*cmpptr) (const char *, const char *, size_t), bool anyend)
|
||||
{
|
||||
char *endt = t->name + t->len;
|
||||
char *endq = qn + len;
|
||||
char *tn;
|
||||
int lent,
|
||||
lenq;
|
||||
bool isok;
|
||||
|
||||
while( (qn=getlexem(qn,endq,&lenq)) != NULL ) {
|
||||
tn=t->name;
|
||||
while ((qn = getlexem(qn, endq, &lenq)) != NULL)
|
||||
{
|
||||
tn = t->name;
|
||||
isok = false;
|
||||
while( (tn=getlexem(tn,endt,&lent)) != NULL ) {
|
||||
if (
|
||||
while ((tn = getlexem(tn, endt, &lent)) != NULL)
|
||||
{
|
||||
if (
|
||||
(
|
||||
lent == lenq ||
|
||||
( lent > lenq && anyend )
|
||||
) &&
|
||||
(*cmpptr)(qn,tn,lenq) == 0 ) {
|
||||
|
||||
isok = true;
|
||||
lent == lenq ||
|
||||
(lent > lenq && anyend)
|
||||
) &&
|
||||
(*cmpptr) (qn, tn, lenq) == 0)
|
||||
{
|
||||
|
||||
isok = true;
|
||||
break;
|
||||
}
|
||||
tn += lent;
|
||||
}
|
||||
|
||||
if ( !isok )
|
||||
if (!isok)
|
||||
return false;
|
||||
qn += lenq;
|
||||
}
|
||||
@@ -70,27 +77,32 @@ compare_subnode( ltree_level *t, char *qn, int len, int (*cmpptr)(const char *,c
|
||||
}
|
||||
|
||||
static bool
|
||||
checkLevel( lquery_level *curq, ltree_level *curt ) {
|
||||
int (*cmpptr)(const char *,const char *,size_t);
|
||||
checkLevel(lquery_level * curq, ltree_level * curt)
|
||||
{
|
||||
int (*cmpptr) (const char *, const char *, size_t);
|
||||
lquery_variant *curvar = LQL_FIRST(curq);
|
||||
int i;
|
||||
|
||||
for(i=0;i<curq->numvar;i++) {
|
||||
cmpptr = ( curvar->flag & LVAR_INCASE ) ? strncasecmp : strncmp;
|
||||
int i;
|
||||
|
||||
if ( curvar->flag & LVAR_SUBLEXEM ) {
|
||||
if ( compare_subnode(curt, curvar->name, curvar->len, cmpptr, (curvar->flag & LVAR_ANYEND) ) )
|
||||
for (i = 0; i < curq->numvar; i++)
|
||||
{
|
||||
cmpptr = (curvar->flag & LVAR_INCASE) ? strncasecmp : strncmp;
|
||||
|
||||
if (curvar->flag & LVAR_SUBLEXEM)
|
||||
{
|
||||
if (compare_subnode(curt, curvar->name, curvar->len, cmpptr, (curvar->flag & LVAR_ANYEND)))
|
||||
return true;
|
||||
} else if (
|
||||
(
|
||||
curvar->len == curt->len ||
|
||||
( curt->len > curvar->len && (curvar->flag & LVAR_ANYEND) )
|
||||
) &&
|
||||
(*cmpptr)( curvar->name, curt->name, curvar->len) == 0 ) {
|
||||
}
|
||||
else if (
|
||||
(
|
||||
curvar->len == curt->len ||
|
||||
(curt->len > curvar->len && (curvar->flag & LVAR_ANYEND))
|
||||
) &&
|
||||
(*cmpptr) (curvar->name, curt->name, curvar->len) == 0)
|
||||
{
|
||||
|
||||
return true;
|
||||
}
|
||||
curvar = LVAR_NEXT(curvar);
|
||||
curvar = LVAR_NEXT(curvar);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -102,78 +114,97 @@ printFieldNot(FieldNot *fn ) {
|
||||
elog(NOTICE,"posQ:%d lenQ:%d posT:%d lenT:%d", fn->posq,fn->nq,fn->post,fn->nt);
|
||||
fn++;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
static bool
|
||||
checkCond( lquery_level *curq, int query_numlevel, ltree_level *curt, int tree_numlevel, FieldNot *ptr ) {
|
||||
uint32 low_pos=0,high_pos=0,cur_tpos=0;
|
||||
int tlen = tree_numlevel, qlen = query_numlevel;
|
||||
int isok;
|
||||
lquery_level *prevq=NULL;
|
||||
ltree_level *prevt=NULL;
|
||||
checkCond(lquery_level * curq, int query_numlevel, ltree_level * curt, int tree_numlevel, FieldNot * ptr)
|
||||
{
|
||||
uint32 low_pos = 0,
|
||||
high_pos = 0,
|
||||
cur_tpos = 0;
|
||||
int tlen = tree_numlevel,
|
||||
qlen = query_numlevel;
|
||||
int isok;
|
||||
lquery_level *prevq = NULL;
|
||||
ltree_level *prevt = NULL;
|
||||
|
||||
while( tlen >0 && qlen>0 ) {
|
||||
if ( curq->numvar ) {
|
||||
while (tlen > 0 && qlen > 0)
|
||||
{
|
||||
if (curq->numvar)
|
||||
{
|
||||
prevt = curt;
|
||||
while ( cur_tpos < low_pos ) {
|
||||
while (cur_tpos < low_pos)
|
||||
{
|
||||
curt = LEVEL_NEXT(curt);
|
||||
tlen--;
|
||||
cur_tpos++;
|
||||
if ( tlen==0 )
|
||||
if (tlen == 0)
|
||||
return false;
|
||||
if ( ptr && ptr->q )
|
||||
if (ptr && ptr->q)
|
||||
ptr->nt++;
|
||||
}
|
||||
|
||||
if ( ptr && curq->flag & LQL_NOT ) {
|
||||
if ( !(prevq && prevq->numvar == 0) )
|
||||
|
||||
if (ptr && curq->flag & LQL_NOT)
|
||||
{
|
||||
if (!(prevq && prevq->numvar == 0))
|
||||
prevq = curq;
|
||||
if ( ptr->q == NULL ) {
|
||||
if (ptr->q == NULL)
|
||||
{
|
||||
ptr->t = prevt;
|
||||
ptr->q = prevq;
|
||||
ptr->nt=1;
|
||||
ptr->nq=1 + ( (prevq==curq) ? 0 : 1 );
|
||||
ptr->posq = query_numlevel - qlen - ( (prevq==curq) ? 0 : 1 );
|
||||
ptr->nt = 1;
|
||||
ptr->nq = 1 + ((prevq == curq) ? 0 : 1);
|
||||
ptr->posq = query_numlevel - qlen - ((prevq == curq) ? 0 : 1);
|
||||
ptr->post = cur_tpos;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr->nt++;
|
||||
ptr->nq++;
|
||||
}
|
||||
|
||||
if ( qlen == 1 && ptr->q->numvar==0 )
|
||||
ptr->nt = tree_numlevel - ptr->post;
|
||||
if (qlen == 1 && ptr->q->numvar == 0)
|
||||
ptr->nt = tree_numlevel - ptr->post;
|
||||
curt = LEVEL_NEXT(curt);
|
||||
tlen--;
|
||||
cur_tpos++;
|
||||
if ( high_pos < cur_tpos )
|
||||
if (high_pos < cur_tpos)
|
||||
high_pos++;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
isok = false;
|
||||
while( cur_tpos <= high_pos && tlen > 0 && !isok) {
|
||||
while (cur_tpos <= high_pos && tlen > 0 && !isok)
|
||||
{
|
||||
isok = checkLevel(curq, curt);
|
||||
curt = LEVEL_NEXT(curt);
|
||||
tlen--;
|
||||
cur_tpos++;
|
||||
if ( !isok && ptr )
|
||||
if (!isok && ptr)
|
||||
ptr->nt++;
|
||||
}
|
||||
if ( !isok )
|
||||
if (!isok)
|
||||
return false;
|
||||
|
||||
if (ptr && ptr->q) {
|
||||
if ( checkCond(ptr->q,ptr->nq,ptr->t,ptr->nt,NULL) )
|
||||
if (ptr && ptr->q)
|
||||
{
|
||||
if (checkCond(ptr->q, ptr->nq, ptr->t, ptr->nt, NULL))
|
||||
return false;
|
||||
ptr->q = NULL;
|
||||
}
|
||||
low_pos=cur_tpos; high_pos=cur_tpos;
|
||||
low_pos = cur_tpos;
|
||||
high_pos = cur_tpos;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
low_pos = cur_tpos + curq->low;
|
||||
high_pos = cur_tpos + curq->high;
|
||||
if ( ptr && ptr->q ) {
|
||||
if (ptr && ptr->q)
|
||||
{
|
||||
ptr->nq++;
|
||||
if ( qlen==1 )
|
||||
if (qlen == 1)
|
||||
ptr->nt = tree_numlevel - ptr->post;
|
||||
}
|
||||
}
|
||||
@@ -181,16 +212,20 @@ checkCond( lquery_level *curq, int query_numlevel, ltree_level *curt, int tree_n
|
||||
prevq = curq;
|
||||
curq = LQL_NEXT(curq);
|
||||
qlen--;
|
||||
}
|
||||
}
|
||||
|
||||
if ( low_pos > tree_numlevel || tree_numlevel > high_pos )
|
||||
if (low_pos > tree_numlevel || tree_numlevel > high_pos)
|
||||
return false;
|
||||
|
||||
while( qlen>0 ) {
|
||||
if ( curq->numvar ) {
|
||||
if ( ! (curq->flag & LQL_NOT) )
|
||||
while (qlen > 0)
|
||||
{
|
||||
if (curq->numvar)
|
||||
{
|
||||
if (!(curq->flag & LQL_NOT))
|
||||
return false;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
low_pos = cur_tpos + curq->low;
|
||||
high_pos = cur_tpos + curq->high;
|
||||
}
|
||||
@@ -199,42 +234,47 @@ checkCond( lquery_level *curq, int query_numlevel, ltree_level *curt, int tree_n
|
||||
qlen--;
|
||||
}
|
||||
|
||||
if ( low_pos > tree_numlevel || tree_numlevel > high_pos )
|
||||
if (low_pos > tree_numlevel || tree_numlevel > high_pos)
|
||||
return false;
|
||||
|
||||
if ( ptr && ptr->q && checkCond(ptr->q,ptr->nq,ptr->t,ptr->nt,NULL) )
|
||||
|
||||
if (ptr && ptr->q && checkCond(ptr->q, ptr->nq, ptr->t, ptr->nt, NULL))
|
||||
return false;
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Datum
|
||||
ltq_regex(PG_FUNCTION_ARGS) {
|
||||
ltree *tree = PG_GETARG_LTREE(0);
|
||||
lquery *query = PG_GETARG_LQUERY(1);
|
||||
bool res= false;
|
||||
ltq_regex(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ltree *tree = PG_GETARG_LTREE(0);
|
||||
lquery *query = PG_GETARG_LQUERY(1);
|
||||
bool res = false;
|
||||
|
||||
if ( query->flag & LQUERY_HASNOT ) {
|
||||
if (query->flag & LQUERY_HASNOT)
|
||||
{
|
||||
FieldNot fn;
|
||||
|
||||
fn.q=NULL;
|
||||
fn.q = NULL;
|
||||
|
||||
res = checkCond( LQUERY_FIRST(query), query->numlevel,
|
||||
LTREE_FIRST(tree), tree->numlevel, &fn );
|
||||
} else {
|
||||
res = checkCond( LQUERY_FIRST(query), query->numlevel,
|
||||
LTREE_FIRST(tree), tree->numlevel, NULL );
|
||||
res = checkCond(LQUERY_FIRST(query), query->numlevel,
|
||||
LTREE_FIRST(tree), tree->numlevel, &fn);
|
||||
}
|
||||
else
|
||||
{
|
||||
res = checkCond(LQUERY_FIRST(query), query->numlevel,
|
||||
LTREE_FIRST(tree), tree->numlevel, NULL);
|
||||
}
|
||||
|
||||
PG_FREE_IF_COPY(tree,0);
|
||||
PG_FREE_IF_COPY(query,1);
|
||||
PG_RETURN_BOOL(res);
|
||||
PG_FREE_IF_COPY(tree, 0);
|
||||
PG_FREE_IF_COPY(query, 1);
|
||||
PG_RETURN_BOOL(res);
|
||||
}
|
||||
|
||||
Datum
|
||||
ltq_rregex(PG_FUNCTION_ARGS) {
|
||||
PG_RETURN_DATUM( DirectFunctionCall2( ltq_regex,
|
||||
PG_GETARG_DATUM(1),
|
||||
PG_GETARG_DATUM(0)
|
||||
) );
|
||||
Datum
|
||||
ltq_rregex(PG_FUNCTION_ARGS)
|
||||
{
|
||||
PG_RETURN_DATUM(DirectFunctionCall2(ltq_regex,
|
||||
PG_GETARG_DATUM(1),
|
||||
PG_GETARG_DATUM(0)
|
||||
));
|
||||
}
|
||||
|
@@ -6,19 +6,21 @@
|
||||
#include "utils/palloc.h"
|
||||
#include "utils/builtins.h"
|
||||
|
||||
typedef struct {
|
||||
uint8 len;
|
||||
char name[1];
|
||||
} ltree_level;
|
||||
typedef struct
|
||||
{
|
||||
uint8 len;
|
||||
char name[1];
|
||||
} ltree_level;
|
||||
|
||||
#define LEVEL_HDRSIZE (sizeof(uint8))
|
||||
#define LEVEL_HDRSIZE (sizeof(uint8))
|
||||
#define LEVEL_NEXT(x) ( (ltree_level*)( ((char*)(x)) + MAXALIGN(((ltree_level*)(x))->len + LEVEL_HDRSIZE) ) )
|
||||
|
||||
typedef struct {
|
||||
int32 len;
|
||||
uint16 numlevel;
|
||||
char data[1];
|
||||
} ltree;
|
||||
typedef struct
|
||||
{
|
||||
int32 len;
|
||||
uint16 numlevel;
|
||||
char data[1];
|
||||
} ltree;
|
||||
|
||||
#define LTREE_HDRSIZE MAXALIGN( sizeof(int32) + sizeof(uint16) )
|
||||
#define LTREE_FIRST(x) ( (ltree_level*)( ((char*)(x))+LTREE_HDRSIZE ) )
|
||||
@@ -26,31 +28,33 @@ typedef struct {
|
||||
|
||||
/* lquery */
|
||||
|
||||
typedef struct {
|
||||
int4 val;
|
||||
uint8 len;
|
||||
uint8 flag;
|
||||
char name[1];
|
||||
} lquery_variant;
|
||||
typedef struct
|
||||
{
|
||||
int4 val;
|
||||
uint8 len;
|
||||
uint8 flag;
|
||||
char name[1];
|
||||
} lquery_variant;
|
||||
|
||||
#define LVAR_HDRSIZE MAXALIGN(sizeof(uint8)*2 + sizeof(int4))
|
||||
#define LVAR_NEXT(x) ( (lquery_variant*)( ((char*)(x)) + MAXALIGN(((lquery_variant*)(x))->len) + LVAR_HDRSIZE ) )
|
||||
|
||||
#define LVAR_ANYEND 0x01
|
||||
#define LVAR_INCASE 0x02
|
||||
#define LVAR_ANYEND 0x01
|
||||
#define LVAR_INCASE 0x02
|
||||
#define LVAR_SUBLEXEM 0x04
|
||||
|
||||
typedef struct {
|
||||
uint16 totallen;
|
||||
uint16 flag;
|
||||
uint16 numvar;
|
||||
uint16 low;
|
||||
uint16 high;
|
||||
char variants[1];
|
||||
} lquery_level;
|
||||
typedef struct
|
||||
{
|
||||
uint16 totallen;
|
||||
uint16 flag;
|
||||
uint16 numvar;
|
||||
uint16 low;
|
||||
uint16 high;
|
||||
char variants[1];
|
||||
} lquery_level;
|
||||
|
||||
#define LQL_HDRSIZE MAXALIGN( sizeof(uint16)*5 )
|
||||
#define LQL_NEXT(x) ( (lquery_level*)( ((char*)(x)) + MAXALIGN(((lquery_level*)(x))->totallen) ) )
|
||||
#define LQL_HDRSIZE MAXALIGN( sizeof(uint16)*5 )
|
||||
#define LQL_NEXT(x) ( (lquery_level*)( ((char*)(x)) + MAXALIGN(((lquery_level*)(x))->totallen) ) )
|
||||
#define LQL_FIRST(x) ( (lquery_variant*)( ((char*)(x))+LQL_HDRSIZE ) )
|
||||
|
||||
#define LQL_NOT 0x10
|
||||
@@ -59,29 +63,30 @@ typedef struct {
|
||||
#else
|
||||
#define FLG_CANLOOKSIGN(x) ( ( (x) & ( LQL_NOT | LVAR_ANYEND | LVAR_SUBLEXEM | LVAR_INCASE ) ) == 0 )
|
||||
#endif
|
||||
#define LQL_CANLOOKSIGN(x) FLG_CANLOOKSIGN( ((lquery_level*)(x))->flag )
|
||||
#define LQL_CANLOOKSIGN(x) FLG_CANLOOKSIGN( ((lquery_level*)(x))->flag )
|
||||
|
||||
typedef struct {
|
||||
int32 len;
|
||||
uint16 numlevel;
|
||||
uint16 firstgood;
|
||||
uint16 flag;
|
||||
char data[1];
|
||||
} lquery;
|
||||
typedef struct
|
||||
{
|
||||
int32 len;
|
||||
uint16 numlevel;
|
||||
uint16 firstgood;
|
||||
uint16 flag;
|
||||
char data[1];
|
||||
} lquery;
|
||||
|
||||
#define LQUERY_HDRSIZE MAXALIGN( sizeof(int32) + 3*sizeof(uint16) )
|
||||
#define LQUERY_HDRSIZE MAXALIGN( sizeof(int32) + 3*sizeof(uint16) )
|
||||
#define LQUERY_FIRST(x) ( (lquery_level*)( ((char*)(x))+LQUERY_HDRSIZE ) )
|
||||
|
||||
#define LQUERY_HASNOT 0x01
|
||||
|
||||
#ifndef max
|
||||
#define max(a,b) ((a) > (b) ? (a) : (b))
|
||||
#ifndef max
|
||||
#define max(a,b) ((a) > (b) ? (a) : (b))
|
||||
#endif
|
||||
#ifndef min
|
||||
#define min(a,b) ((a) <= (b) ? (a) : (b))
|
||||
#define min(a,b) ((a) <= (b) ? (a) : (b))
|
||||
#endif
|
||||
#ifndef abs
|
||||
#define abs(a) ((a) < (0) ? -(a) : (a))
|
||||
#define abs(a) ((a) < (0) ? -(a) : (a))
|
||||
#endif
|
||||
#define ISALNUM(x) ( isalnum((unsigned int)(x)) || (x) == '_' )
|
||||
|
||||
@@ -93,75 +98,75 @@ typedef struct {
|
||||
*/
|
||||
typedef struct ITEM
|
||||
{
|
||||
int2 type;
|
||||
int2 left;
|
||||
int4 val;
|
||||
int2 type;
|
||||
int2 left;
|
||||
int4 val;
|
||||
uint8 flag;
|
||||
/* user-friendly value */
|
||||
uint8 length;
|
||||
uint16 distance;
|
||||
} ITEM;
|
||||
/* user-friendly value */
|
||||
uint8 length;
|
||||
uint16 distance;
|
||||
} ITEM;
|
||||
|
||||
/*
|
||||
*Storage:
|
||||
* (len)(size)(array of ITEM)(array of operand in user-friendly form)
|
||||
* (len)(size)(array of ITEM)(array of operand in user-friendly form)
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int4 len;
|
||||
int4 size;
|
||||
char data[1];
|
||||
} ltxtquery;
|
||||
int4 len;
|
||||
int4 size;
|
||||
char data[1];
|
||||
} ltxtquery;
|
||||
|
||||
#define HDRSIZEQT MAXALIGN( 2*sizeof(int4) )
|
||||
#define COMPUTESIZE(size,lenofoperand) ( HDRSIZEQT + size * sizeof(ITEM) + lenofoperand )
|
||||
#define HDRSIZEQT MAXALIGN( 2*sizeof(int4) )
|
||||
#define COMPUTESIZE(size,lenofoperand) ( HDRSIZEQT + size * sizeof(ITEM) + lenofoperand )
|
||||
#define GETQUERY(x) (ITEM*)( (char*)(x)+HDRSIZEQT )
|
||||
#define GETOPERAND(x) ( (char*)GETQUERY(x) + ((ltxtquery*)x)->size * sizeof(ITEM) )
|
||||
#define GETOPERAND(x) ( (char*)GETQUERY(x) + ((ltxtquery*)x)->size * sizeof(ITEM) )
|
||||
|
||||
#define ISOPERATOR(x) ( (x)=='!' || (x)=='&' || (x)=='|' || (x)=='(' || (x)==')' )
|
||||
|
||||
#define END 0
|
||||
#define ERR 1
|
||||
#define VAL 2
|
||||
#define OPR 3
|
||||
#define OPEN 4
|
||||
#define CLOSE 5
|
||||
#define VALTRUE 6 /* for stop words */
|
||||
#define VALFALSE 7
|
||||
#define END 0
|
||||
#define ERR 1
|
||||
#define VAL 2
|
||||
#define OPR 3
|
||||
#define OPEN 4
|
||||
#define CLOSE 5
|
||||
#define VALTRUE 6 /* for stop words */
|
||||
#define VALFALSE 7
|
||||
|
||||
|
||||
/* use in array iterator */
|
||||
Datum ltree_isparent(PG_FUNCTION_ARGS);
|
||||
Datum ltree_risparent(PG_FUNCTION_ARGS);
|
||||
Datum ltq_regex(PG_FUNCTION_ARGS);
|
||||
Datum ltq_rregex(PG_FUNCTION_ARGS);
|
||||
Datum ltxtq_exec(PG_FUNCTION_ARGS);
|
||||
Datum ltxtq_rexec(PG_FUNCTION_ARGS);
|
||||
Datum _ltq_regex(PG_FUNCTION_ARGS);
|
||||
Datum _ltq_rregex(PG_FUNCTION_ARGS);
|
||||
Datum _ltxtq_exec(PG_FUNCTION_ARGS);
|
||||
Datum _ltxtq_rexec(PG_FUNCTION_ARGS);
|
||||
Datum _ltree_isparent(PG_FUNCTION_ARGS);
|
||||
Datum _ltree_risparent(PG_FUNCTION_ARGS);
|
||||
Datum ltree_isparent(PG_FUNCTION_ARGS);
|
||||
Datum ltree_risparent(PG_FUNCTION_ARGS);
|
||||
Datum ltq_regex(PG_FUNCTION_ARGS);
|
||||
Datum ltq_rregex(PG_FUNCTION_ARGS);
|
||||
Datum ltxtq_exec(PG_FUNCTION_ARGS);
|
||||
Datum ltxtq_rexec(PG_FUNCTION_ARGS);
|
||||
Datum _ltq_regex(PG_FUNCTION_ARGS);
|
||||
Datum _ltq_rregex(PG_FUNCTION_ARGS);
|
||||
Datum _ltxtq_exec(PG_FUNCTION_ARGS);
|
||||
Datum _ltxtq_rexec(PG_FUNCTION_ARGS);
|
||||
Datum _ltree_isparent(PG_FUNCTION_ARGS);
|
||||
Datum _ltree_risparent(PG_FUNCTION_ARGS);
|
||||
|
||||
/* Concatenation functions */
|
||||
Datum ltree_addltree(PG_FUNCTION_ARGS);
|
||||
Datum ltree_addtext(PG_FUNCTION_ARGS);
|
||||
Datum ltree_textadd(PG_FUNCTION_ARGS);
|
||||
Datum ltree_addltree(PG_FUNCTION_ARGS);
|
||||
Datum ltree_addtext(PG_FUNCTION_ARGS);
|
||||
Datum ltree_textadd(PG_FUNCTION_ARGS);
|
||||
|
||||
/* Util function */
|
||||
Datum ltree_in(PG_FUNCTION_ARGS);
|
||||
Datum ltree_in(PG_FUNCTION_ARGS);
|
||||
|
||||
bool ltree_execute(ITEM * curitem, void *checkval,
|
||||
bool calcnot, bool (*chkcond) (void *checkval, ITEM * val));
|
||||
bool calcnot, bool (*chkcond) (void *checkval, ITEM * val));
|
||||
|
||||
int ltree_compare(const ltree *a, const ltree *b);
|
||||
bool inner_isparent(const ltree *c, const ltree *p);
|
||||
bool compare_subnode( ltree_level *t, char *q, int len,
|
||||
int (*cmpptr)(const char *,const char *,size_t), bool anyend );
|
||||
ltree* lca_inner(ltree** a, int len);
|
||||
int ltree_compare(const ltree * a, const ltree * b);
|
||||
bool inner_isparent(const ltree * c, const ltree * p);
|
||||
bool compare_subnode(ltree_level * t, char *q, int len,
|
||||
int (*cmpptr) (const char *, const char *, size_t), bool anyend);
|
||||
ltree *lca_inner(ltree ** a, int len);
|
||||
|
||||
#define PG_GETARG_LTREE(x) ((ltree*)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(x))))
|
||||
#define PG_GETARG_LTREE(x) ((ltree*)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(x))))
|
||||
#define PG_GETARG_LQUERY(x) ((lquery*)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(x))))
|
||||
#define PG_GETARG_LTXTQUERY(x) ((ltxtquery*)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(x))))
|
||||
|
||||
@@ -169,7 +174,7 @@ ltree* lca_inner(ltree** a, int len);
|
||||
|
||||
#define BITBYTE 8
|
||||
#define SIGLENINT 8
|
||||
#define SIGLEN ( sizeof(int4)*SIGLENINT )
|
||||
#define SIGLEN ( sizeof(int4)*SIGLENINT )
|
||||
#define SIGLENBIT (SIGLEN*BITBYTE)
|
||||
typedef unsigned char BITVEC[SIGLEN];
|
||||
typedef unsigned char *BITVECP;
|
||||
@@ -195,43 +200,44 @@ typedef unsigned char *BITVECP;
|
||||
/*
|
||||
* type of index key for ltree. Tree are combined B-Tree and R-Tree
|
||||
* Storage:
|
||||
* Leaf pages
|
||||
* Leaf pages
|
||||
* (len)(flag)(ltree)
|
||||
* Non-Leaf
|
||||
* (len)(flag)(sign)(left_ltree)(right_ltree)
|
||||
* (len)(flag)(sign)(left_ltree)(right_ltree)
|
||||
* ALLTRUE: (len)(flag)(left_ltree)(right_ltree)
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
int4 len;
|
||||
uint32 flag;
|
||||
char data[1];
|
||||
} ltree_gist;
|
||||
typedef struct
|
||||
{
|
||||
int4 len;
|
||||
uint32 flag;
|
||||
char data[1];
|
||||
} ltree_gist;
|
||||
|
||||
#define LTG_ONENODE 0x01
|
||||
#define LTG_ALLTRUE 0x02
|
||||
#define LTG_NORIGHT 0x04
|
||||
#define LTG_ONENODE 0x01
|
||||
#define LTG_ALLTRUE 0x02
|
||||
#define LTG_NORIGHT 0x04
|
||||
|
||||
#define LTG_HDRSIZE MAXALIGN( sizeof(int4) + sizeof(uint32) )
|
||||
#define LTG_SIGN(x) ( (BITVECP)( ((char*)(x))+LTG_HDRSIZE ) )
|
||||
#define LTG_NODE(x) ( (ltree*)( ((char*)(x))+LTG_HDRSIZE ) )
|
||||
#define LTG_ISONENODE(x) ( ((ltree_gist*)(x))->flag & LTG_ONENODE )
|
||||
#define LTG_ISALLTRUE(x) ( ((ltree_gist*)(x))->flag & LTG_ALLTRUE )
|
||||
#define LTG_ISNORIGHT(x) ( ((ltree_gist*)(x))->flag & LTG_NORIGHT )
|
||||
#define LTG_HDRSIZE MAXALIGN( sizeof(int4) + sizeof(uint32) )
|
||||
#define LTG_SIGN(x) ( (BITVECP)( ((char*)(x))+LTG_HDRSIZE ) )
|
||||
#define LTG_NODE(x) ( (ltree*)( ((char*)(x))+LTG_HDRSIZE ) )
|
||||
#define LTG_ISONENODE(x) ( ((ltree_gist*)(x))->flag & LTG_ONENODE )
|
||||
#define LTG_ISALLTRUE(x) ( ((ltree_gist*)(x))->flag & LTG_ALLTRUE )
|
||||
#define LTG_ISNORIGHT(x) ( ((ltree_gist*)(x))->flag & LTG_NORIGHT )
|
||||
#define LTG_LNODE(x) ( (ltree*)( ( ((char*)(x))+LTG_HDRSIZE ) + ( LTG_ISALLTRUE(x) ? 0 : SIGLEN ) ) )
|
||||
#define LTG_RENODE(x) ( (ltree*)( ((char*)LTG_LNODE(x)) + LTG_LNODE(x)->len) )
|
||||
#define LTG_RNODE(x) ( LTG_ISNORIGHT(x) ? LTG_LNODE(x) : LTG_RENODE(x) )
|
||||
|
||||
#define LTG_GETLNODE(x) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_LNODE(x) )
|
||||
#define LTG_GETRNODE(x) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_RNODE(x) )
|
||||
#define LTG_GETLNODE(x) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_LNODE(x) )
|
||||
#define LTG_GETRNODE(x) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_RNODE(x) )
|
||||
|
||||
|
||||
/* GiST support for ltree[] */
|
||||
|
||||
#define ASIGLENINT (2*SIGLENINT)
|
||||
#define ASIGLENINT (2*SIGLENINT)
|
||||
#define ASIGLEN (sizeof(int4)*ASIGLENINT)
|
||||
#define ASIGLENBIT (ASIGLEN*BITBYTE)
|
||||
#define ASIGLENBIT (ASIGLEN*BITBYTE)
|
||||
typedef unsigned char ABITVEC[ASIGLEN];
|
||||
|
||||
#define ALOOPBYTE(a) \
|
||||
@@ -249,4 +255,3 @@ typedef unsigned char ABITVEC[ASIGLEN];
|
||||
/* type of key is the same to ltree_gist */
|
||||
|
||||
#endif
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* GiST support for ltree
|
||||
* GiST support for ltree
|
||||
* Teodor Sigaev <teodor@stack.net>
|
||||
*/
|
||||
|
||||
@@ -10,252 +10,297 @@
|
||||
|
||||
#include "crc32.h"
|
||||
|
||||
PG_FUNCTION_INFO_V1( ltree_gist_in );
|
||||
Datum ltree_gist_in(PG_FUNCTION_ARGS);
|
||||
PG_FUNCTION_INFO_V1( ltree_gist_out );
|
||||
Datum ltree_gist_out(PG_FUNCTION_ARGS);
|
||||
PG_FUNCTION_INFO_V1(ltree_gist_in);
|
||||
Datum ltree_gist_in(PG_FUNCTION_ARGS);
|
||||
|
||||
PG_FUNCTION_INFO_V1(ltree_gist_out);
|
||||
Datum ltree_gist_out(PG_FUNCTION_ARGS);
|
||||
|
||||
Datum
|
||||
ltree_gist_in(PG_FUNCTION_ARGS) {
|
||||
elog(ERROR,"Unimplemented");
|
||||
ltree_gist_in(PG_FUNCTION_ARGS)
|
||||
{
|
||||
elog(ERROR, "Unimplemented");
|
||||
PG_RETURN_DATUM(0);
|
||||
}
|
||||
|
||||
Datum
|
||||
ltree_gist_out(PG_FUNCTION_ARGS) {
|
||||
elog(ERROR,"Unimplemented");
|
||||
ltree_gist_out(PG_FUNCTION_ARGS)
|
||||
{
|
||||
elog(ERROR, "Unimplemented");
|
||||
PG_RETURN_DATUM(0);
|
||||
}
|
||||
|
||||
PG_FUNCTION_INFO_V1( ltree_compress );
|
||||
Datum ltree_compress(PG_FUNCTION_ARGS);
|
||||
PG_FUNCTION_INFO_V1( ltree_decompress );
|
||||
Datum ltree_decompress(PG_FUNCTION_ARGS);
|
||||
PG_FUNCTION_INFO_V1( ltree_same );
|
||||
Datum ltree_same(PG_FUNCTION_ARGS);
|
||||
PG_FUNCTION_INFO_V1( ltree_union );
|
||||
Datum ltree_union(PG_FUNCTION_ARGS);
|
||||
PG_FUNCTION_INFO_V1( ltree_penalty );
|
||||
Datum ltree_penalty(PG_FUNCTION_ARGS);
|
||||
PG_FUNCTION_INFO_V1( ltree_picksplit );
|
||||
Datum ltree_picksplit(PG_FUNCTION_ARGS);
|
||||
PG_FUNCTION_INFO_V1( ltree_consistent );
|
||||
Datum ltree_consistent(PG_FUNCTION_ARGS);
|
||||
PG_FUNCTION_INFO_V1(ltree_compress);
|
||||
Datum ltree_compress(PG_FUNCTION_ARGS);
|
||||
|
||||
PG_FUNCTION_INFO_V1(ltree_decompress);
|
||||
Datum ltree_decompress(PG_FUNCTION_ARGS);
|
||||
|
||||
PG_FUNCTION_INFO_V1(ltree_same);
|
||||
Datum ltree_same(PG_FUNCTION_ARGS);
|
||||
|
||||
PG_FUNCTION_INFO_V1(ltree_union);
|
||||
Datum ltree_union(PG_FUNCTION_ARGS);
|
||||
|
||||
PG_FUNCTION_INFO_V1(ltree_penalty);
|
||||
Datum ltree_penalty(PG_FUNCTION_ARGS);
|
||||
|
||||
PG_FUNCTION_INFO_V1(ltree_picksplit);
|
||||
Datum ltree_picksplit(PG_FUNCTION_ARGS);
|
||||
|
||||
PG_FUNCTION_INFO_V1(ltree_consistent);
|
||||
Datum ltree_consistent(PG_FUNCTION_ARGS);
|
||||
|
||||
#define ISEQ(a,b) ( (a)->numlevel == (b)->numlevel && ltree_compare(a,b)==0 )
|
||||
#define GETENTRY(vec,pos) ((ltree_gist *) DatumGetPointer(((GISTENTRY *) VARDATA(vec))[(pos)].key))
|
||||
|
||||
Datum
|
||||
ltree_compress(PG_FUNCTION_ARGS) {
|
||||
GISTENTRY *entry = (GISTENTRY *)PG_GETARG_POINTER(0);
|
||||
Datum
|
||||
ltree_compress(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
|
||||
GISTENTRY *retval = entry;
|
||||
|
||||
if ( entry->leafkey ) { /* ltree */
|
||||
ltree_gist *key;
|
||||
ltree *val = (ltree*)DatumGetPointer(PG_DETOAST_DATUM(entry->key));
|
||||
int4 len = LTG_HDRSIZE + val->len;
|
||||
if (entry->leafkey)
|
||||
{ /* ltree */
|
||||
ltree_gist *key;
|
||||
ltree *val = (ltree *) DatumGetPointer(PG_DETOAST_DATUM(entry->key));
|
||||
int4 len = LTG_HDRSIZE + val->len;
|
||||
|
||||
key = (ltree_gist*)palloc( len );
|
||||
key = (ltree_gist *) palloc(len);
|
||||
key->len = len;
|
||||
key->flag = LTG_ONENODE;
|
||||
memcpy( (void*)LTG_NODE(key), (void*)val, val->len);
|
||||
memcpy((void *) LTG_NODE(key), (void *) val, val->len);
|
||||
|
||||
if ( PointerGetDatum(val) != entry->key )
|
||||
if (PointerGetDatum(val) != entry->key)
|
||||
pfree(val);
|
||||
|
||||
retval = (GISTENTRY*)palloc( sizeof(GISTENTRY) );
|
||||
retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
|
||||
gistentryinit(*retval, PointerGetDatum(key),
|
||||
entry->rel, entry->page,
|
||||
entry->offset, key->len, FALSE);
|
||||
entry->rel, entry->page,
|
||||
entry->offset, key->len, FALSE);
|
||||
}
|
||||
PG_RETURN_POINTER(retval);
|
||||
}
|
||||
|
||||
Datum
|
||||
ltree_decompress(PG_FUNCTION_ARGS) {
|
||||
GISTENTRY *entry = (GISTENTRY *)PG_GETARG_POINTER(0);
|
||||
ltree_gist *key = (ltree_gist*)DatumGetPointer( PG_DETOAST_DATUM(entry->key) );
|
||||
Datum
|
||||
ltree_decompress(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
|
||||
ltree_gist *key = (ltree_gist *) DatumGetPointer(PG_DETOAST_DATUM(entry->key));
|
||||
|
||||
if (PointerGetDatum(key) != entry->key)
|
||||
{
|
||||
GISTENTRY *retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
|
||||
|
||||
if ( PointerGetDatum(key) != entry->key ) {
|
||||
GISTENTRY *retval = (GISTENTRY*)palloc(sizeof(GISTENTRY));
|
||||
gistentryinit(*retval, PointerGetDatum(key),
|
||||
entry->rel, entry->page,
|
||||
entry->offset, key->len, FALSE);
|
||||
entry->rel, entry->page,
|
||||
entry->offset, key->len, FALSE);
|
||||
PG_RETURN_POINTER(retval);
|
||||
}
|
||||
PG_RETURN_POINTER(entry);
|
||||
PG_RETURN_POINTER(entry);
|
||||
}
|
||||
|
||||
Datum
|
||||
ltree_same(PG_FUNCTION_ARGS) {
|
||||
ltree_gist* a=(ltree_gist*)PG_GETARG_POINTER(0);
|
||||
ltree_gist* b=(ltree_gist*)PG_GETARG_POINTER(1);
|
||||
bool *result = (bool *)PG_GETARG_POINTER(2);
|
||||
Datum
|
||||
ltree_same(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ltree_gist *a = (ltree_gist *) PG_GETARG_POINTER(0);
|
||||
ltree_gist *b = (ltree_gist *) PG_GETARG_POINTER(1);
|
||||
bool *result = (bool *) PG_GETARG_POINTER(2);
|
||||
|
||||
*result = false;
|
||||
if ( LTG_ISONENODE(a) != LTG_ISONENODE(b) )
|
||||
PG_RETURN_POINTER(result);
|
||||
|
||||
if ( LTG_ISONENODE(a) ) {
|
||||
*result = ( ISEQ(LTG_NODE(a), LTG_NODE(b)) ) ? true : false;
|
||||
} else {
|
||||
int4 i;
|
||||
BITVECP sa=LTG_SIGN(a), sb=LTG_SIGN(b);
|
||||
|
||||
if ( LTG_ISALLTRUE(a) != LTG_ISALLTRUE(b) )
|
||||
PG_RETURN_POINTER(result);
|
||||
if (LTG_ISONENODE(a) != LTG_ISONENODE(b))
|
||||
PG_RETURN_POINTER(result);
|
||||
|
||||
if ( !ISEQ(LTG_LNODE(a), LTG_LNODE(b)) )
|
||||
PG_RETURN_POINTER(result);
|
||||
if ( !ISEQ(LTG_RNODE(a), LTG_RNODE(b)) )
|
||||
if (LTG_ISONENODE(a))
|
||||
*result = (ISEQ(LTG_NODE(a), LTG_NODE(b))) ? true : false;
|
||||
else
|
||||
{
|
||||
int4 i;
|
||||
BITVECP sa = LTG_SIGN(a),
|
||||
sb = LTG_SIGN(b);
|
||||
|
||||
if (LTG_ISALLTRUE(a) != LTG_ISALLTRUE(b))
|
||||
PG_RETURN_POINTER(result);
|
||||
|
||||
if (!ISEQ(LTG_LNODE(a), LTG_LNODE(b)))
|
||||
PG_RETURN_POINTER(result);
|
||||
if (!ISEQ(LTG_RNODE(a), LTG_RNODE(b)))
|
||||
PG_RETURN_POINTER(result);
|
||||
|
||||
*result = true;
|
||||
if ( !LTG_ISALLTRUE(a) )
|
||||
if (!LTG_ISALLTRUE(a))
|
||||
LOOPBYTE(
|
||||
if ( sa[i] != sb[i] ) {
|
||||
*result = false;
|
||||
break;
|
||||
}
|
||||
);
|
||||
if (sa[i] != sb[i])
|
||||
{
|
||||
*result = false;
|
||||
break;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
PG_RETURN_POINTER(result);
|
||||
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
static void
|
||||
hashing(BITVECP sign, ltree *t) {
|
||||
int tlen = t->numlevel;
|
||||
hashing(BITVECP sign, ltree * t)
|
||||
{
|
||||
int tlen = t->numlevel;
|
||||
ltree_level *cur = LTREE_FIRST(t);
|
||||
int hash;
|
||||
int hash;
|
||||
|
||||
while(tlen > 0) {
|
||||
hash = ltree_crc32_sz( cur->name, cur->len );
|
||||
HASH( sign, hash );
|
||||
while (tlen > 0)
|
||||
{
|
||||
hash = ltree_crc32_sz(cur->name, cur->len);
|
||||
HASH(sign, hash);
|
||||
cur = LEVEL_NEXT(cur);
|
||||
tlen--;
|
||||
}
|
||||
}
|
||||
|
||||
Datum
|
||||
ltree_union(PG_FUNCTION_ARGS) {
|
||||
bytea *entryvec = (bytea *) PG_GETARG_POINTER(0);
|
||||
int *size = (int *) PG_GETARG_POINTER(1);
|
||||
BITVEC base;
|
||||
int4 len = (VARSIZE(entryvec) - VARHDRSZ) / sizeof(GISTENTRY);
|
||||
int4 i,j;
|
||||
ltree_gist *result,*cur;
|
||||
ltree *left=NULL, *right=NULL, *curtree;
|
||||
bool isalltrue = false;
|
||||
bool isleqr;
|
||||
Datum
|
||||
ltree_union(PG_FUNCTION_ARGS)
|
||||
{
|
||||
bytea *entryvec = (bytea *) PG_GETARG_POINTER(0);
|
||||
int *size = (int *) PG_GETARG_POINTER(1);
|
||||
BITVEC base;
|
||||
int4 len = (VARSIZE(entryvec) - VARHDRSZ) / sizeof(GISTENTRY);
|
||||
int4 i,
|
||||
j;
|
||||
ltree_gist *result,
|
||||
*cur;
|
||||
ltree *left = NULL,
|
||||
*right = NULL,
|
||||
*curtree;
|
||||
bool isalltrue = false;
|
||||
bool isleqr;
|
||||
|
||||
MemSet( (void*)base, 0, sizeof(BITVEC) );
|
||||
for(j=0;j<len;j++) {
|
||||
MemSet((void *) base, 0, sizeof(BITVEC));
|
||||
for (j = 0; j < len; j++)
|
||||
{
|
||||
cur = GETENTRY(entryvec, j);
|
||||
if ( LTG_ISONENODE(cur) ) {
|
||||
if (LTG_ISONENODE(cur))
|
||||
{
|
||||
curtree = LTG_NODE(cur);
|
||||
hashing(base,curtree);
|
||||
if ( !left || ltree_compare( left, curtree ) > 0 )
|
||||
left = curtree;
|
||||
if ( !right || ltree_compare( right, curtree ) < 0 )
|
||||
hashing(base, curtree);
|
||||
if (!left || ltree_compare(left, curtree) > 0)
|
||||
left = curtree;
|
||||
if (!right || ltree_compare(right, curtree) < 0)
|
||||
right = curtree;
|
||||
} else {
|
||||
if ( isalltrue || LTG_ISALLTRUE(cur) )
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isalltrue || LTG_ISALLTRUE(cur))
|
||||
isalltrue = true;
|
||||
else {
|
||||
BITVECP sc=LTG_SIGN(cur);
|
||||
else
|
||||
{
|
||||
BITVECP sc = LTG_SIGN(cur);
|
||||
|
||||
LOOPBYTE(
|
||||
((unsigned char*)base)[i] |= sc[i];
|
||||
((unsigned char *) base)[i] |= sc[i];
|
||||
);
|
||||
}
|
||||
|
||||
curtree = LTG_LNODE(cur);
|
||||
if ( !left || ltree_compare( left, curtree ) > 0 )
|
||||
left = curtree;
|
||||
if (!left || ltree_compare(left, curtree) > 0)
|
||||
left = curtree;
|
||||
curtree = LTG_RNODE(cur);
|
||||
if ( !right || ltree_compare( right, curtree ) < 0 )
|
||||
if (!right || ltree_compare(right, curtree) < 0)
|
||||
right = curtree;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( isalltrue == false ) {
|
||||
|
||||
if (isalltrue == false)
|
||||
{
|
||||
isalltrue = true;
|
||||
LOOPBYTE(
|
||||
if ( ((unsigned char*)base)[i] != 0xff ) {
|
||||
isalltrue = false;
|
||||
break;
|
||||
}
|
||||
);
|
||||
if (((unsigned char *) base)[i] != 0xff)
|
||||
{
|
||||
isalltrue = false;
|
||||
break;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
isleqr = ( left==right || ISEQ(left,right) ) ? true : false;
|
||||
*size = LTG_HDRSIZE + ( (isalltrue) ? 0 : SIGLEN ) + left->len + ( (isleqr) ? 0 : right->len );
|
||||
isleqr = (left == right || ISEQ(left, right)) ? true : false;
|
||||
*size = LTG_HDRSIZE + ((isalltrue) ? 0 : SIGLEN) + left->len + ((isleqr) ? 0 : right->len);
|
||||
|
||||
result = (ltree_gist*)palloc( *size );
|
||||
result = (ltree_gist *) palloc(*size);
|
||||
result->len = *size;
|
||||
result->flag = 0;
|
||||
|
||||
if ( isalltrue )
|
||||
if (isalltrue)
|
||||
result->flag |= LTG_ALLTRUE;
|
||||
else
|
||||
memcpy( (void*)LTG_SIGN(result), base, SIGLEN );
|
||||
memcpy((void *) LTG_SIGN(result), base, SIGLEN);
|
||||
|
||||
memcpy( (void*)LTG_LNODE(result), (void*)left, left->len );
|
||||
if ( isleqr )
|
||||
memcpy((void *) LTG_LNODE(result), (void *) left, left->len);
|
||||
if (isleqr)
|
||||
result->flag |= LTG_NORIGHT;
|
||||
else
|
||||
memcpy( (void*)LTG_RNODE(result), (void*)right, right->len );
|
||||
memcpy((void *) LTG_RNODE(result), (void *) right, right->len);
|
||||
|
||||
PG_RETURN_POINTER(result);
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
Datum
|
||||
ltree_penalty(PG_FUNCTION_ARGS) {
|
||||
ltree_gist *origval = (ltree_gist*)DatumGetPointer( ( (GISTENTRY *)PG_GETARG_POINTER(0) )->key );
|
||||
ltree_gist *newval = (ltree_gist*)DatumGetPointer( ( (GISTENTRY *)PG_GETARG_POINTER(1) )->key );
|
||||
float *penalty = (float *) PG_GETARG_POINTER(2);
|
||||
int4 cmpr,cmpl;
|
||||
Datum
|
||||
ltree_penalty(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ltree_gist *origval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(0))->key);
|
||||
ltree_gist *newval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(1))->key);
|
||||
float *penalty = (float *) PG_GETARG_POINTER(2);
|
||||
int4 cmpr,
|
||||
cmpl;
|
||||
|
||||
cmpl = ltree_compare( LTG_GETLNODE(origval), LTG_GETLNODE(newval) );
|
||||
cmpr = ltree_compare( LTG_GETRNODE(newval), LTG_GETRNODE(origval));
|
||||
cmpl = ltree_compare(LTG_GETLNODE(origval), LTG_GETLNODE(newval));
|
||||
cmpr = ltree_compare(LTG_GETRNODE(newval), LTG_GETRNODE(origval));
|
||||
|
||||
*penalty = max( cmpl, 0 ) + max( cmpr, 0 );
|
||||
*penalty = max(cmpl, 0) + max(cmpr, 0);
|
||||
|
||||
PG_RETURN_POINTER(penalty);
|
||||
}
|
||||
|
||||
/* used for sorting */
|
||||
typedef struct rix {
|
||||
int index;
|
||||
ltree *r;
|
||||
} RIX;
|
||||
typedef struct rix
|
||||
{
|
||||
int index;
|
||||
ltree *r;
|
||||
} RIX;
|
||||
|
||||
static int
|
||||
treekey_cmp(const void *a, const void *b) {
|
||||
treekey_cmp(const void *a, const void *b)
|
||||
{
|
||||
return ltree_compare(
|
||||
((RIX *) a)->r,
|
||||
((RIX *) b)->r
|
||||
((RIX *) a)->r,
|
||||
((RIX *) b)->r
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Datum
|
||||
ltree_picksplit(PG_FUNCTION_ARGS) {
|
||||
bytea *entryvec = (bytea*) PG_GETARG_POINTER(0);
|
||||
GIST_SPLITVEC *v = (GIST_SPLITVEC*) PG_GETARG_POINTER(1);
|
||||
Datum
|
||||
ltree_picksplit(PG_FUNCTION_ARGS)
|
||||
{
|
||||
bytea *entryvec = (bytea *) PG_GETARG_POINTER(0);
|
||||
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
|
||||
OffsetNumber j;
|
||||
int4 i;
|
||||
RIX *array;
|
||||
int4 i;
|
||||
RIX *array;
|
||||
OffsetNumber maxoff;
|
||||
int nbytes;
|
||||
int size;
|
||||
ltree *lu_l,*lu_r, *ru_l, *ru_r;
|
||||
ltree_gist *lu, *ru;
|
||||
BITVEC ls,rs;
|
||||
bool lisat=false, risat=false, isleqr;
|
||||
|
||||
memset( (void*)ls,0,sizeof(BITVEC) );
|
||||
memset( (void*)rs,0,sizeof(BITVEC) );
|
||||
int nbytes;
|
||||
int size;
|
||||
ltree *lu_l,
|
||||
*lu_r,
|
||||
*ru_l,
|
||||
*ru_r;
|
||||
ltree_gist *lu,
|
||||
*ru;
|
||||
BITVEC ls,
|
||||
rs;
|
||||
bool lisat = false,
|
||||
risat = false,
|
||||
isleqr;
|
||||
|
||||
memset((void *) ls, 0, sizeof(BITVEC));
|
||||
memset((void *) rs, 0, sizeof(BITVEC));
|
||||
maxoff = ((VARSIZE(entryvec) - VARHDRSZ) / sizeof(GISTENTRY)) - 1;
|
||||
nbytes = (maxoff + 2) * sizeof(OffsetNumber);
|
||||
v->spl_left = (OffsetNumber *) palloc(nbytes);
|
||||
@@ -263,109 +308,124 @@ ltree_picksplit(PG_FUNCTION_ARGS) {
|
||||
v->spl_nleft = 0;
|
||||
v->spl_nright = 0;
|
||||
array = (RIX *) palloc(sizeof(RIX) * (maxoff + 1));
|
||||
|
||||
|
||||
/* copy the data into RIXes, and sort the RIXes */
|
||||
for (j = FirstOffsetNumber; j <= maxoff; j = OffsetNumberNext(j)) {
|
||||
for (j = FirstOffsetNumber; j <= maxoff; j = OffsetNumberNext(j))
|
||||
{
|
||||
array[j].index = j;
|
||||
lu = GETENTRY( entryvec, j ); /* use as tmp val */
|
||||
lu = GETENTRY(entryvec, j); /* use as tmp val */
|
||||
array[j].r = LTG_GETLNODE(lu);
|
||||
}
|
||||
|
||||
qsort((void *) &array[FirstOffsetNumber], maxoff - FirstOffsetNumber + 1,
|
||||
sizeof(RIX), treekey_cmp);
|
||||
sizeof(RIX), treekey_cmp);
|
||||
|
||||
lu_l = lu_r = ru_l = ru_r = NULL;
|
||||
for (j = FirstOffsetNumber; j <= maxoff; j = OffsetNumberNext(j)) {
|
||||
lu = GETENTRY( entryvec, array[j].index ); /* use as tmp val */
|
||||
if (j <= (maxoff - FirstOffsetNumber + 1) / 2) {
|
||||
for (j = FirstOffsetNumber; j <= maxoff; j = OffsetNumberNext(j))
|
||||
{
|
||||
lu = GETENTRY(entryvec, array[j].index); /* use as tmp val */
|
||||
if (j <= (maxoff - FirstOffsetNumber + 1) / 2)
|
||||
{
|
||||
v->spl_left[v->spl_nleft] = array[j].index;
|
||||
v->spl_nleft++;
|
||||
if ( lu_r==NULL || ltree_compare( LTG_GETRNODE(lu), lu_r ) > 0 )
|
||||
if (lu_r == NULL || ltree_compare(LTG_GETRNODE(lu), lu_r) > 0)
|
||||
lu_r = LTG_GETRNODE(lu);
|
||||
if ( LTG_ISONENODE(lu) )
|
||||
hashing(ls,LTG_NODE(lu));
|
||||
else {
|
||||
if ( lisat || LTG_ISALLTRUE(lu) )
|
||||
if (LTG_ISONENODE(lu))
|
||||
hashing(ls, LTG_NODE(lu));
|
||||
else
|
||||
{
|
||||
if (lisat || LTG_ISALLTRUE(lu))
|
||||
lisat = true;
|
||||
else {
|
||||
BITVECP sc=LTG_SIGN(lu);
|
||||
else
|
||||
{
|
||||
BITVECP sc = LTG_SIGN(lu);
|
||||
|
||||
LOOPBYTE(
|
||||
((unsigned char*)ls)[i] |= sc[i];
|
||||
((unsigned char *) ls)[i] |= sc[i];
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
v->spl_right[v->spl_nright] = array[j].index;
|
||||
v->spl_nright++;
|
||||
if ( ru_r==NULL || ltree_compare( LTG_GETRNODE(lu), ru_r ) > 0 )
|
||||
if (ru_r == NULL || ltree_compare(LTG_GETRNODE(lu), ru_r) > 0)
|
||||
ru_r = LTG_GETRNODE(lu);
|
||||
if ( LTG_ISONENODE(lu) )
|
||||
hashing(rs,LTG_NODE(lu));
|
||||
else {
|
||||
if ( risat || LTG_ISALLTRUE(lu) )
|
||||
if (LTG_ISONENODE(lu))
|
||||
hashing(rs, LTG_NODE(lu));
|
||||
else
|
||||
{
|
||||
if (risat || LTG_ISALLTRUE(lu))
|
||||
risat = true;
|
||||
else {
|
||||
BITVECP sc=LTG_SIGN(lu);
|
||||
else
|
||||
{
|
||||
BITVECP sc = LTG_SIGN(lu);
|
||||
|
||||
LOOPBYTE(
|
||||
((unsigned char*)rs)[i] |= sc[i];
|
||||
((unsigned char *) rs)[i] |= sc[i];
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( lisat == false ) {
|
||||
|
||||
if (lisat == false)
|
||||
{
|
||||
lisat = true;
|
||||
LOOPBYTE(
|
||||
if ( ((unsigned char*)ls)[i] != 0xff ) {
|
||||
lisat = false;
|
||||
break;
|
||||
}
|
||||
);
|
||||
if (((unsigned char *) ls)[i] != 0xff)
|
||||
{
|
||||
lisat = false;
|
||||
break;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if ( risat == false ) {
|
||||
if (risat == false)
|
||||
{
|
||||
risat = true;
|
||||
LOOPBYTE(
|
||||
if ( ((unsigned char*)rs)[i] != 0xff ) {
|
||||
risat = false;
|
||||
break;
|
||||
}
|
||||
);
|
||||
if (((unsigned char *) rs)[i] != 0xff)
|
||||
{
|
||||
risat = false;
|
||||
break;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
lu_l = LTG_GETLNODE( GETENTRY( entryvec, array[FirstOffsetNumber].index ) );
|
||||
isleqr = ( lu_l==lu_r || ISEQ(lu_l,lu_r) ) ? true : false;
|
||||
size = LTG_HDRSIZE + ( (lisat) ? 0 : SIGLEN ) + lu_l->len + ( (isleqr) ? 0 : lu_r->len );
|
||||
lu = (ltree_gist*)palloc( size );
|
||||
lu_l = LTG_GETLNODE(GETENTRY(entryvec, array[FirstOffsetNumber].index));
|
||||
isleqr = (lu_l == lu_r || ISEQ(lu_l, lu_r)) ? true : false;
|
||||
size = LTG_HDRSIZE + ((lisat) ? 0 : SIGLEN) + lu_l->len + ((isleqr) ? 0 : lu_r->len);
|
||||
lu = (ltree_gist *) palloc(size);
|
||||
lu->len = size;
|
||||
lu->flag = 0;
|
||||
if ( lisat )
|
||||
if (lisat)
|
||||
lu->flag |= LTG_ALLTRUE;
|
||||
else
|
||||
memcpy( (void*)LTG_SIGN(lu), ls, SIGLEN );
|
||||
memcpy( (void*)LTG_LNODE(lu), (void*)lu_l, lu_l->len );
|
||||
if ( isleqr )
|
||||
memcpy((void *) LTG_SIGN(lu), ls, SIGLEN);
|
||||
memcpy((void *) LTG_LNODE(lu), (void *) lu_l, lu_l->len);
|
||||
if (isleqr)
|
||||
lu->flag |= LTG_NORIGHT;
|
||||
else
|
||||
memcpy( (void*)LTG_RNODE(lu), (void*)lu_r, lu_r->len );
|
||||
memcpy((void *) LTG_RNODE(lu), (void *) lu_r, lu_r->len);
|
||||
|
||||
|
||||
ru_l = LTG_GETLNODE( GETENTRY( entryvec, array[ 1 + ((maxoff - FirstOffsetNumber + 1) / 2) ].index ) );
|
||||
isleqr = ( ru_l==ru_r || ISEQ(ru_l,ru_r) ) ? true : false;
|
||||
size = LTG_HDRSIZE + ( (risat) ? 0 : SIGLEN ) + ru_l->len + ( (isleqr) ? 0 : ru_r->len );
|
||||
ru = (ltree_gist*)palloc( size );
|
||||
ru_l = LTG_GETLNODE(GETENTRY(entryvec, array[1 + ((maxoff - FirstOffsetNumber + 1) / 2)].index));
|
||||
isleqr = (ru_l == ru_r || ISEQ(ru_l, ru_r)) ? true : false;
|
||||
size = LTG_HDRSIZE + ((risat) ? 0 : SIGLEN) + ru_l->len + ((isleqr) ? 0 : ru_r->len);
|
||||
ru = (ltree_gist *) palloc(size);
|
||||
ru->len = size;
|
||||
ru->flag = 0;
|
||||
if ( risat )
|
||||
if (risat)
|
||||
ru->flag |= LTG_ALLTRUE;
|
||||
else
|
||||
memcpy( (void*)LTG_SIGN(ru), rs, SIGLEN );
|
||||
memcpy( (void*)LTG_LNODE(ru), (void*)ru_l, ru_l->len );
|
||||
if ( isleqr )
|
||||
memcpy((void *) LTG_SIGN(ru), rs, SIGLEN);
|
||||
memcpy((void *) LTG_LNODE(ru), (void *) ru_l, ru_l->len);
|
||||
if (isleqr)
|
||||
ru->flag |= LTG_NORIGHT;
|
||||
else
|
||||
memcpy( (void*)LTG_RNODE(ru), (void*)ru_r, ru_r->len );
|
||||
memcpy((void *) LTG_RNODE(ru), (void *) ru_r, ru_r->len);
|
||||
|
||||
pfree(array);
|
||||
v->spl_ldatum = PointerGetDatum(lu);
|
||||
@@ -375,13 +435,16 @@ ltree_picksplit(PG_FUNCTION_ARGS) {
|
||||
}
|
||||
|
||||
static bool
|
||||
gist_isparent(ltree_gist *key, ltree *query) {
|
||||
int4 numlevel = query->numlevel;
|
||||
int i;
|
||||
gist_isparent(ltree_gist * key, ltree * query)
|
||||
{
|
||||
int4 numlevel = query->numlevel;
|
||||
int i;
|
||||
|
||||
for(i=query->numlevel;i>=0;i--) {
|
||||
query->numlevel=i;
|
||||
if ( ltree_compare(query,LTG_GETLNODE(key)) >=0 && ltree_compare(query,LTG_GETRNODE(key)) <= 0 ) {
|
||||
for (i = query->numlevel; i >= 0; i--)
|
||||
{
|
||||
query->numlevel = i;
|
||||
if (ltree_compare(query, LTG_GETLNODE(key)) >= 0 && ltree_compare(query, LTG_GETRNODE(key)) <= 0)
|
||||
{
|
||||
query->numlevel = numlevel;
|
||||
return true;
|
||||
}
|
||||
@@ -392,23 +455,24 @@ gist_isparent(ltree_gist *key, ltree *query) {
|
||||
}
|
||||
|
||||
static bool
|
||||
gist_ischild(ltree_gist *key, ltree *query) {
|
||||
ltree *left = LTG_GETLNODE(key);
|
||||
ltree *right = LTG_GETRNODE(key);
|
||||
int4 numlevelL = left->numlevel;
|
||||
int4 numlevelR = right->numlevel;
|
||||
bool res = true;
|
||||
gist_ischild(ltree_gist * key, ltree * query)
|
||||
{
|
||||
ltree *left = LTG_GETLNODE(key);
|
||||
ltree *right = LTG_GETRNODE(key);
|
||||
int4 numlevelL = left->numlevel;
|
||||
int4 numlevelR = right->numlevel;
|
||||
bool res = true;
|
||||
|
||||
if ( numlevelL > query->numlevel )
|
||||
if (numlevelL > query->numlevel)
|
||||
left->numlevel = query->numlevel;
|
||||
|
||||
if ( ltree_compare(query,left) < 0 )
|
||||
if (ltree_compare(query, left) < 0)
|
||||
res = false;
|
||||
|
||||
if ( numlevelR > query->numlevel )
|
||||
if (numlevelR > query->numlevel)
|
||||
right->numlevel = query->numlevel;
|
||||
|
||||
if ( res && ltree_compare(query,right) > 0 )
|
||||
if (res && ltree_compare(query, right) > 0)
|
||||
res = false;
|
||||
|
||||
left->numlevel = numlevelL;
|
||||
@@ -417,29 +481,35 @@ gist_ischild(ltree_gist *key, ltree *query) {
|
||||
}
|
||||
|
||||
static bool
|
||||
gist_qe(ltree_gist *key, lquery* query) {
|
||||
lquery_level *curq = LQUERY_FIRST(query);
|
||||
BITVECP sign = LTG_SIGN(key);
|
||||
int qlen = query->numlevel;
|
||||
gist_qe(ltree_gist * key, lquery * query)
|
||||
{
|
||||
lquery_level *curq = LQUERY_FIRST(query);
|
||||
BITVECP sign = LTG_SIGN(key);
|
||||
int qlen = query->numlevel;
|
||||
|
||||
if ( LTG_ISALLTRUE(key) )
|
||||
if (LTG_ISALLTRUE(key))
|
||||
return true;
|
||||
|
||||
while( qlen>0 ) {
|
||||
if ( curq->numvar && LQL_CANLOOKSIGN(curq) ) {
|
||||
bool isexist=false;
|
||||
int vlen = curq->numvar;
|
||||
while (qlen > 0)
|
||||
{
|
||||
if (curq->numvar && LQL_CANLOOKSIGN(curq))
|
||||
{
|
||||
bool isexist = false;
|
||||
int vlen = curq->numvar;
|
||||
lquery_variant *curv = LQL_FIRST(curq);
|
||||
while( vlen>0 ) {
|
||||
if ( GETBIT( sign, HASHVAL( curv->val ) ) ) {
|
||||
isexist=true;
|
||||
|
||||
while (vlen > 0)
|
||||
{
|
||||
if (GETBIT(sign, HASHVAL(curv->val)))
|
||||
{
|
||||
isexist = true;
|
||||
break;
|
||||
}
|
||||
curv = LVAR_NEXT(curv);
|
||||
vlen--;
|
||||
}
|
||||
if ( !isexist )
|
||||
return false;
|
||||
if (!isexist)
|
||||
return false;
|
||||
}
|
||||
|
||||
curq = LQL_NEXT(curq);
|
||||
@@ -450,22 +520,27 @@ gist_qe(ltree_gist *key, lquery* query) {
|
||||
}
|
||||
|
||||
static int
|
||||
gist_tqcmp(ltree* t, lquery* q) {
|
||||
gist_tqcmp(ltree * t, lquery * q)
|
||||
{
|
||||
ltree_level *al = LTREE_FIRST(t);
|
||||
lquery_level *ql = LQUERY_FIRST(q);
|
||||
lquery_variant *bl;
|
||||
int an = t->numlevel;
|
||||
int bn = q->firstgood;
|
||||
int res = 0;
|
||||
int an = t->numlevel;
|
||||
int bn = q->firstgood;
|
||||
int res = 0;
|
||||
|
||||
while( an>0 && bn>0 ) {
|
||||
while (an > 0 && bn > 0)
|
||||
{
|
||||
bl = LQL_FIRST(ql);
|
||||
if ( (res = strncmp( al->name, bl->name, min(al->len, bl->len))) == 0 ) {
|
||||
if ( al->len != bl->len )
|
||||
if ((res = strncmp(al->name, bl->name, min(al->len, bl->len))) == 0)
|
||||
{
|
||||
if (al->len != bl->len)
|
||||
return al->len - bl->len;
|
||||
} else
|
||||
}
|
||||
else
|
||||
return res;
|
||||
an--; bn--;
|
||||
an--;
|
||||
bn--;
|
||||
al = LEVEL_NEXT(al);
|
||||
ql = LQL_NEXT(ql);
|
||||
}
|
||||
@@ -474,26 +549,27 @@ gist_tqcmp(ltree* t, lquery* q) {
|
||||
}
|
||||
|
||||
static bool
|
||||
gist_between(ltree_gist *key, lquery* query) {
|
||||
ltree *left = LTG_GETLNODE(key);
|
||||
ltree *right = LTG_GETRNODE(key);
|
||||
int4 numlevelL = left->numlevel;
|
||||
int4 numlevelR = right->numlevel;
|
||||
bool res = true;
|
||||
gist_between(ltree_gist * key, lquery * query)
|
||||
{
|
||||
ltree *left = LTG_GETLNODE(key);
|
||||
ltree *right = LTG_GETRNODE(key);
|
||||
int4 numlevelL = left->numlevel;
|
||||
int4 numlevelR = right->numlevel;
|
||||
bool res = true;
|
||||
|
||||
if ( query->firstgood == 0 )
|
||||
if (query->firstgood == 0)
|
||||
return true;
|
||||
|
||||
if ( numlevelL > query->firstgood )
|
||||
if (numlevelL > query->firstgood)
|
||||
left->numlevel = query->firstgood;
|
||||
|
||||
if ( gist_tqcmp(left,query) > 0 )
|
||||
if (gist_tqcmp(left, query) > 0)
|
||||
res = false;
|
||||
|
||||
if ( numlevelR > query->firstgood )
|
||||
if (numlevelR > query->firstgood)
|
||||
right->numlevel = query->firstgood;
|
||||
|
||||
if ( res && gist_tqcmp(right,query) < 0 )
|
||||
if (res && gist_tqcmp(right, query) < 0)
|
||||
res = false;
|
||||
|
||||
left->numlevel = numlevelL;
|
||||
@@ -502,99 +578,102 @@ gist_between(ltree_gist *key, lquery* query) {
|
||||
}
|
||||
|
||||
static bool
|
||||
checkcondition_bit(void *checkval, ITEM* val ) {
|
||||
return ( FLG_CANLOOKSIGN(val->flag) ) ? GETBIT( checkval, HASHVAL( val->val ) ) : true;
|
||||
checkcondition_bit(void *checkval, ITEM * val)
|
||||
{
|
||||
return (FLG_CANLOOKSIGN(val->flag)) ? GETBIT(checkval, HASHVAL(val->val)) : true;
|
||||
}
|
||||
|
||||
static bool
|
||||
gist_qtxt(ltree_gist *key, ltxtquery* query) {
|
||||
if ( LTG_ISALLTRUE(key) )
|
||||
gist_qtxt(ltree_gist * key, ltxtquery * query)
|
||||
{
|
||||
if (LTG_ISALLTRUE(key))
|
||||
return true;
|
||||
|
||||
|
||||
return ltree_execute(
|
||||
GETQUERY(query),
|
||||
(void*)LTG_SIGN(key), false,
|
||||
checkcondition_bit
|
||||
);
|
||||
GETQUERY(query),
|
||||
(void *) LTG_SIGN(key), false,
|
||||
checkcondition_bit
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Datum
|
||||
ltree_consistent(PG_FUNCTION_ARGS) {
|
||||
GISTENTRY *entry = (GISTENTRY*)PG_GETARG_POINTER(0);
|
||||
char *query = (char*)DatumGetPointer( PG_DETOAST_DATUM(PG_GETARG_DATUM(1)) );
|
||||
ltree_gist *key = (ltree_gist*)DatumGetPointer( entry->key );
|
||||
Datum
|
||||
ltree_consistent(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
|
||||
char *query = (char *) DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(1)));
|
||||
ltree_gist *key = (ltree_gist *) DatumGetPointer(entry->key);
|
||||
StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
|
||||
bool res = false;
|
||||
bool res = false;
|
||||
|
||||
#ifndef assert_enabled
|
||||
#ifndef assert_enabled
|
||||
#define assert_enabled 0
|
||||
#endif
|
||||
|
||||
switch( strategy ) {
|
||||
|
||||
switch (strategy)
|
||||
{
|
||||
case BTLessStrategyNumber:
|
||||
res = ( GIST_LEAF( entry ) ) ?
|
||||
( ltree_compare((ltree*)query,LTG_NODE(key)) > 0 )
|
||||
res = (GIST_LEAF(entry)) ?
|
||||
(ltree_compare((ltree *) query, LTG_NODE(key)) > 0)
|
||||
:
|
||||
( ltree_compare((ltree*)query,LTG_GETLNODE(key)) >= 0 );
|
||||
(ltree_compare((ltree *) query, LTG_GETLNODE(key)) >= 0);
|
||||
break;
|
||||
case BTLessEqualStrategyNumber:
|
||||
res = ( ltree_compare((ltree*)query,LTG_GETLNODE(key)) >= 0 );
|
||||
res = (ltree_compare((ltree *) query, LTG_GETLNODE(key)) >= 0);
|
||||
break;
|
||||
case BTEqualStrategyNumber:
|
||||
if ( GIST_LEAF( entry ) )
|
||||
res = ( ltree_compare((ltree*)query,LTG_NODE(key)) == 0 );
|
||||
if (GIST_LEAF(entry))
|
||||
res = (ltree_compare((ltree *) query, LTG_NODE(key)) == 0);
|
||||
else
|
||||
res = (
|
||||
ltree_compare((ltree*)query,LTG_GETLNODE(key)) >= 0
|
||||
&&
|
||||
ltree_compare((ltree*)query,LTG_GETRNODE(key)) <= 0
|
||||
);
|
||||
ltree_compare((ltree *) query, LTG_GETLNODE(key)) >= 0
|
||||
&&
|
||||
ltree_compare((ltree *) query, LTG_GETRNODE(key)) <= 0
|
||||
);
|
||||
break;
|
||||
case BTGreaterEqualStrategyNumber:
|
||||
res = ( ltree_compare((ltree*)query,LTG_GETRNODE(key)) <= 0 );
|
||||
res = (ltree_compare((ltree *) query, LTG_GETRNODE(key)) <= 0);
|
||||
break;
|
||||
case BTGreaterStrategyNumber:
|
||||
res = ( GIST_LEAF( entry ) ) ?
|
||||
( ltree_compare((ltree*)query,LTG_GETRNODE(key)) < 0 )
|
||||
res = (GIST_LEAF(entry)) ?
|
||||
(ltree_compare((ltree *) query, LTG_GETRNODE(key)) < 0)
|
||||
:
|
||||
( ltree_compare((ltree*)query,LTG_GETRNODE(key)) <= 0 );
|
||||
(ltree_compare((ltree *) query, LTG_GETRNODE(key)) <= 0);
|
||||
break;
|
||||
case 10:
|
||||
res = ( GIST_LEAF( entry ) ) ?
|
||||
inner_isparent( (ltree*)query, LTG_NODE(key) )
|
||||
res = (GIST_LEAF(entry)) ?
|
||||
inner_isparent((ltree *) query, LTG_NODE(key))
|
||||
:
|
||||
gist_isparent( key, (ltree*)query);
|
||||
gist_isparent(key, (ltree *) query);
|
||||
break;
|
||||
case 11:
|
||||
res = ( GIST_LEAF( entry ) ) ?
|
||||
inner_isparent( LTG_NODE(key), (ltree*)query)
|
||||
res = (GIST_LEAF(entry)) ?
|
||||
inner_isparent(LTG_NODE(key), (ltree *) query)
|
||||
:
|
||||
gist_ischild( key, (ltree*)query);
|
||||
gist_ischild(key, (ltree *) query);
|
||||
break;
|
||||
case 12:
|
||||
case 13:
|
||||
if ( GIST_LEAF( entry ) )
|
||||
res = DatumGetBool( DirectFunctionCall2( ltq_regex,
|
||||
PointerGetDatum( LTG_NODE(key) ),
|
||||
PointerGetDatum( (lquery*)query )
|
||||
) );
|
||||
else
|
||||
res = ( gist_qe(key, (lquery*)query) && gist_between(key, (lquery*)query) );
|
||||
break;
|
||||
if (GIST_LEAF(entry))
|
||||
res = DatumGetBool(DirectFunctionCall2(ltq_regex,
|
||||
PointerGetDatum(LTG_NODE(key)),
|
||||
PointerGetDatum((lquery *) query)
|
||||
));
|
||||
else
|
||||
res = (gist_qe(key, (lquery *) query) && gist_between(key, (lquery *) query));
|
||||
break;
|
||||
case 14:
|
||||
case 15:
|
||||
if ( GIST_LEAF( entry ) )
|
||||
res = DatumGetBool( DirectFunctionCall2( ltxtq_exec,
|
||||
PointerGetDatum( LTG_NODE(key) ),
|
||||
PointerGetDatum( (lquery*)query )
|
||||
) );
|
||||
else
|
||||
res = gist_qtxt(key, (ltxtquery*)query);
|
||||
break;
|
||||
if (GIST_LEAF(entry))
|
||||
res = DatumGetBool(DirectFunctionCall2(ltxtq_exec,
|
||||
PointerGetDatum(LTG_NODE(key)),
|
||||
PointerGetDatum((lquery *) query)
|
||||
));
|
||||
else
|
||||
res = gist_qtxt(key, (ltxtquery *) query);
|
||||
break;
|
||||
default:
|
||||
elog(ERROR,"Unknown StrategyNumber: %d", strategy);
|
||||
elog(ERROR, "Unknown StrategyNumber: %d", strategy);
|
||||
}
|
||||
PG_RETURN_BOOL(res);
|
||||
}
|
||||
|
||||
|
@@ -5,91 +5,110 @@
|
||||
|
||||
#include "ltree.h"
|
||||
#include <ctype.h>
|
||||
#include "crc32.h"
|
||||
#include "crc32.h"
|
||||
|
||||
PG_FUNCTION_INFO_V1(ltree_in);
|
||||
Datum ltree_in(PG_FUNCTION_ARGS);
|
||||
Datum ltree_in(PG_FUNCTION_ARGS);
|
||||
|
||||
PG_FUNCTION_INFO_V1(ltree_out);
|
||||
Datum ltree_out(PG_FUNCTION_ARGS);
|
||||
Datum ltree_out(PG_FUNCTION_ARGS);
|
||||
|
||||
PG_FUNCTION_INFO_V1(lquery_in);
|
||||
Datum lquery_in(PG_FUNCTION_ARGS);
|
||||
Datum lquery_in(PG_FUNCTION_ARGS);
|
||||
|
||||
PG_FUNCTION_INFO_V1(lquery_out);
|
||||
Datum lquery_out(PG_FUNCTION_ARGS);
|
||||
Datum lquery_out(PG_FUNCTION_ARGS);
|
||||
|
||||
|
||||
#define UNCHAR elog(ERROR,"Syntax error in position %d near '%c'", (int)(ptr-buf), *ptr)
|
||||
|
||||
typedef struct {
|
||||
char* start;
|
||||
int len;
|
||||
int flag;
|
||||
} nodeitem;
|
||||
typedef struct
|
||||
{
|
||||
char *start;
|
||||
int len;
|
||||
int flag;
|
||||
} nodeitem;
|
||||
|
||||
#define LTPRS_WAITNAME 0
|
||||
#define LTPRS_WAITDELIM 1
|
||||
#define LTPRS_WAITDELIM 1
|
||||
|
||||
Datum
|
||||
ltree_in(PG_FUNCTION_ARGS) {
|
||||
char *buf = (char *) PG_GETARG_POINTER(0);
|
||||
char *ptr;
|
||||
nodeitem *list, *lptr;
|
||||
int num=0, totallen = 0;
|
||||
int state = LTPRS_WAITNAME;
|
||||
ltree *result;
|
||||
ltree_level *curlevel;
|
||||
Datum
|
||||
ltree_in(PG_FUNCTION_ARGS)
|
||||
{
|
||||
char *buf = (char *) PG_GETARG_POINTER(0);
|
||||
char *ptr;
|
||||
nodeitem *list,
|
||||
*lptr;
|
||||
int num = 0,
|
||||
totallen = 0;
|
||||
int state = LTPRS_WAITNAME;
|
||||
ltree *result;
|
||||
ltree_level *curlevel;
|
||||
|
||||
ptr=buf;
|
||||
while( *ptr ) {
|
||||
if ( *ptr == '.' )
|
||||
ptr = buf;
|
||||
while (*ptr)
|
||||
{
|
||||
if (*ptr == '.')
|
||||
num++;
|
||||
ptr++;
|
||||
}
|
||||
|
||||
list = lptr = (nodeitem*) palloc( sizeof(nodeitem)*(num+1) );
|
||||
ptr=buf;
|
||||
while( *ptr ) {
|
||||
if ( state == LTPRS_WAITNAME ) {
|
||||
if ( ISALNUM(*ptr) ) {
|
||||
list = lptr = (nodeitem *) palloc(sizeof(nodeitem) * (num + 1));
|
||||
ptr = buf;
|
||||
while (*ptr)
|
||||
{
|
||||
if (state == LTPRS_WAITNAME)
|
||||
{
|
||||
if (ISALNUM(*ptr))
|
||||
{
|
||||
lptr->start = ptr;
|
||||
state = LTPRS_WAITDELIM;
|
||||
} else
|
||||
}
|
||||
else
|
||||
UNCHAR;
|
||||
} else if ( state == LTPRS_WAITDELIM ) {
|
||||
if ( *ptr == '.' ) {
|
||||
}
|
||||
else if (state == LTPRS_WAITDELIM)
|
||||
{
|
||||
if (*ptr == '.')
|
||||
{
|
||||
lptr->len = ptr - lptr->start;
|
||||
if ( lptr->len > 255 )
|
||||
elog(ERROR,"Name of level is too long (%d, must be < 256) in position %d",
|
||||
lptr->len, (int)(lptr->start - buf));
|
||||
if (lptr->len > 255)
|
||||
elog(ERROR, "Name of level is too long (%d, must be < 256) in position %d",
|
||||
lptr->len, (int) (lptr->start - buf));
|
||||
totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
|
||||
lptr++;
|
||||
state = LTPRS_WAITNAME;
|
||||
} else if ( !ISALNUM(*ptr) )
|
||||
}
|
||||
else if (!ISALNUM(*ptr))
|
||||
UNCHAR;
|
||||
} else
|
||||
elog(ERROR,"Inner error in parser");
|
||||
}
|
||||
else
|
||||
elog(ERROR, "Inner error in parser");
|
||||
ptr++;
|
||||
}
|
||||
|
||||
if ( state == LTPRS_WAITDELIM ) {
|
||||
if (state == LTPRS_WAITDELIM)
|
||||
{
|
||||
lptr->len = ptr - lptr->start;
|
||||
if ( lptr->len > 255 )
|
||||
elog(ERROR,"Name of level is too long (%d, must be < 256) in position %d",
|
||||
lptr->len, (int)(lptr->start - buf));
|
||||
if (lptr->len > 255)
|
||||
elog(ERROR, "Name of level is too long (%d, must be < 256) in position %d",
|
||||
lptr->len, (int) (lptr->start - buf));
|
||||
totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
|
||||
lptr++;
|
||||
} else if ( ! (state == LTPRS_WAITNAME && lptr == list) )
|
||||
elog(ERROR,"Unexpected end of line");
|
||||
}
|
||||
else if (!(state == LTPRS_WAITNAME && lptr == list))
|
||||
elog(ERROR, "Unexpected end of line");
|
||||
|
||||
result = (ltree*)palloc( LTREE_HDRSIZE + totallen );
|
||||
result = (ltree *) palloc(LTREE_HDRSIZE + totallen);
|
||||
result->len = LTREE_HDRSIZE + totallen;
|
||||
result->numlevel = lptr-list;
|
||||
result->numlevel = lptr - list;
|
||||
curlevel = LTREE_FIRST(result);
|
||||
lptr=list;
|
||||
while( lptr-list < result->numlevel ) {
|
||||
lptr = list;
|
||||
while (lptr - list < result->numlevel)
|
||||
{
|
||||
curlevel->len = (uint8) lptr->len;
|
||||
memcpy( curlevel->name, lptr->start, lptr->len);
|
||||
curlevel = LEVEL_NEXT(curlevel);
|
||||
memcpy(curlevel->name, lptr->start, lptr->len);
|
||||
curlevel = LEVEL_NEXT(curlevel);
|
||||
lptr++;
|
||||
}
|
||||
|
||||
@@ -97,254 +116,323 @@ ltree_in(PG_FUNCTION_ARGS) {
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
Datum
|
||||
ltree_out(PG_FUNCTION_ARGS) {
|
||||
ltree *in = PG_GETARG_LTREE(0);
|
||||
char *buf,*ptr;
|
||||
int i;
|
||||
ltree_level *curlevel;
|
||||
|
||||
ptr = buf = (char*)palloc( in->len );
|
||||
Datum
|
||||
ltree_out(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ltree *in = PG_GETARG_LTREE(0);
|
||||
char *buf,
|
||||
*ptr;
|
||||
int i;
|
||||
ltree_level *curlevel;
|
||||
|
||||
ptr = buf = (char *) palloc(in->len);
|
||||
curlevel = LTREE_FIRST(in);
|
||||
for(i=0;i<in->numlevel;i++) {
|
||||
if ( i!=0 ) {
|
||||
for (i = 0; i < in->numlevel; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
*ptr = '.';
|
||||
ptr++;
|
||||
}
|
||||
memcpy( ptr, curlevel->name, curlevel->len );
|
||||
ptr+=curlevel->len;
|
||||
memcpy(ptr, curlevel->name, curlevel->len);
|
||||
ptr += curlevel->len;
|
||||
curlevel = LEVEL_NEXT(curlevel);
|
||||
}
|
||||
|
||||
*ptr='\0';
|
||||
PG_FREE_IF_COPY(in,0);
|
||||
*ptr = '\0';
|
||||
PG_FREE_IF_COPY(in, 0);
|
||||
|
||||
PG_RETURN_POINTER(buf);
|
||||
}
|
||||
|
||||
#define LQPRS_WAITLEVEL 0
|
||||
#define LQPRS_WAITDELIM 1
|
||||
#define LQPRS_WAITOPEN 2
|
||||
#define LQPRS_WAITFNUM 3
|
||||
#define LQPRS_WAITSNUM 4
|
||||
#define LQPRS_WAITND 5
|
||||
#define LQPRS_WAITCLOSE 6
|
||||
#define LQPRS_WAITLEVEL 0
|
||||
#define LQPRS_WAITDELIM 1
|
||||
#define LQPRS_WAITOPEN 2
|
||||
#define LQPRS_WAITFNUM 3
|
||||
#define LQPRS_WAITSNUM 4
|
||||
#define LQPRS_WAITND 5
|
||||
#define LQPRS_WAITCLOSE 6
|
||||
#define LQPRS_WAITEND 7
|
||||
#define LQPRS_WAITVAR 8
|
||||
|
||||
|
||||
#define GETVAR(x) ( *((nodeitem**)LQL_FIRST(x)) )
|
||||
#define ITEMSIZE MAXALIGN(LQL_HDRSIZE+sizeof(nodeitem*))
|
||||
#define NEXTLEV(x) ( (lquery_level*)( ((char*)(x)) + ITEMSIZE) )
|
||||
#define GETVAR(x) ( *((nodeitem**)LQL_FIRST(x)) )
|
||||
#define ITEMSIZE MAXALIGN(LQL_HDRSIZE+sizeof(nodeitem*))
|
||||
#define NEXTLEV(x) ( (lquery_level*)( ((char*)(x)) + ITEMSIZE) )
|
||||
|
||||
Datum
|
||||
lquery_in(PG_FUNCTION_ARGS) {
|
||||
char *buf = (char *) PG_GETARG_POINTER(0);
|
||||
char *ptr;
|
||||
int num=0, totallen = 0, numOR=0;
|
||||
int state = LQPRS_WAITLEVEL;
|
||||
lquery *result;
|
||||
nodeitem *lptr=NULL;
|
||||
lquery_level *cur,*curqlevel, *tmpql;
|
||||
lquery_variant *lrptr=NULL;
|
||||
bool hasnot=false;
|
||||
bool wasbad=false;
|
||||
Datum
|
||||
lquery_in(PG_FUNCTION_ARGS)
|
||||
{
|
||||
char *buf = (char *) PG_GETARG_POINTER(0);
|
||||
char *ptr;
|
||||
int num = 0,
|
||||
totallen = 0,
|
||||
numOR = 0;
|
||||
int state = LQPRS_WAITLEVEL;
|
||||
lquery *result;
|
||||
nodeitem *lptr = NULL;
|
||||
lquery_level *cur,
|
||||
*curqlevel,
|
||||
*tmpql;
|
||||
lquery_variant *lrptr = NULL;
|
||||
bool hasnot = false;
|
||||
bool wasbad = false;
|
||||
|
||||
ptr=buf;
|
||||
while( *ptr ) {
|
||||
if ( *ptr == '.' )
|
||||
ptr = buf;
|
||||
while (*ptr)
|
||||
{
|
||||
if (*ptr == '.')
|
||||
num++;
|
||||
else if ( *ptr == '|' )
|
||||
else if (*ptr == '|')
|
||||
numOR++;
|
||||
ptr++;
|
||||
}
|
||||
|
||||
|
||||
num++;
|
||||
curqlevel = tmpql = (lquery_level*) palloc( ITEMSIZE*num );
|
||||
memset((void*)tmpql,0, ITEMSIZE*num );
|
||||
ptr=buf;
|
||||
while( *ptr ) {
|
||||
if ( state==LQPRS_WAITLEVEL ) {
|
||||
if ( ISALNUM(*ptr) ) {
|
||||
GETVAR(curqlevel) = lptr = (nodeitem*)palloc( sizeof(nodeitem)*(numOR+1) );
|
||||
memset((void*)GETVAR(curqlevel), 0,sizeof(nodeitem)*(numOR+1) );
|
||||
curqlevel = tmpql = (lquery_level *) palloc(ITEMSIZE * num);
|
||||
memset((void *) tmpql, 0, ITEMSIZE * num);
|
||||
ptr = buf;
|
||||
while (*ptr)
|
||||
{
|
||||
if (state == LQPRS_WAITLEVEL)
|
||||
{
|
||||
if (ISALNUM(*ptr))
|
||||
{
|
||||
GETVAR(curqlevel) = lptr = (nodeitem *) palloc(sizeof(nodeitem) * (numOR + 1));
|
||||
memset((void *) GETVAR(curqlevel), 0, sizeof(nodeitem) * (numOR + 1));
|
||||
lptr->start = ptr;
|
||||
state = LQPRS_WAITDELIM;
|
||||
curqlevel->numvar = 1;
|
||||
} else if ( *ptr == '!' ) {
|
||||
GETVAR(curqlevel) = lptr = (nodeitem*)palloc( sizeof(nodeitem)*(numOR+1) );
|
||||
memset((void*)GETVAR(curqlevel), 0,sizeof(nodeitem)*(numOR+1) );
|
||||
lptr->start = ptr+1;
|
||||
}
|
||||
else if (*ptr == '!')
|
||||
{
|
||||
GETVAR(curqlevel) = lptr = (nodeitem *) palloc(sizeof(nodeitem) * (numOR + 1));
|
||||
memset((void *) GETVAR(curqlevel), 0, sizeof(nodeitem) * (numOR + 1));
|
||||
lptr->start = ptr + 1;
|
||||
state = LQPRS_WAITDELIM;
|
||||
curqlevel->numvar = 1;
|
||||
curqlevel->flag |= LQL_NOT;
|
||||
hasnot=true;
|
||||
} else if ( *ptr == '*' ) {
|
||||
hasnot = true;
|
||||
}
|
||||
else if (*ptr == '*')
|
||||
state = LQPRS_WAITOPEN;
|
||||
} else
|
||||
else
|
||||
UNCHAR;
|
||||
} else if ( state==LQPRS_WAITVAR ) {
|
||||
if ( ISALNUM(*ptr) ) {
|
||||
}
|
||||
else if (state == LQPRS_WAITVAR)
|
||||
{
|
||||
if (ISALNUM(*ptr))
|
||||
{
|
||||
lptr++;
|
||||
lptr->start = ptr;
|
||||
state = LQPRS_WAITDELIM;
|
||||
curqlevel->numvar++;
|
||||
} else
|
||||
}
|
||||
else
|
||||
UNCHAR;
|
||||
} else if ( state==LQPRS_WAITDELIM ) {
|
||||
if ( *ptr == '@' ) {
|
||||
if ( lptr->start == ptr )
|
||||
}
|
||||
else if (state == LQPRS_WAITDELIM)
|
||||
{
|
||||
if (*ptr == '@')
|
||||
{
|
||||
if (lptr->start == ptr)
|
||||
UNCHAR;
|
||||
lptr->flag |= LVAR_INCASE;
|
||||
curqlevel->flag |= LVAR_INCASE;
|
||||
} else if ( *ptr == '*' ) {
|
||||
if ( lptr->start == ptr )
|
||||
}
|
||||
else if (*ptr == '*')
|
||||
{
|
||||
if (lptr->start == ptr)
|
||||
UNCHAR;
|
||||
lptr->flag |= LVAR_ANYEND;
|
||||
curqlevel->flag |= LVAR_ANYEND;
|
||||
} else if ( *ptr == '%' ) {
|
||||
if ( lptr->start == ptr )
|
||||
}
|
||||
else if (*ptr == '%')
|
||||
{
|
||||
if (lptr->start == ptr)
|
||||
UNCHAR;
|
||||
lptr->flag |= LVAR_SUBLEXEM;
|
||||
curqlevel->flag |= LVAR_SUBLEXEM;
|
||||
} else if ( *ptr == '|' ) {
|
||||
lptr->len = ptr - lptr->start -
|
||||
( ( lptr->flag & LVAR_SUBLEXEM ) ? 1 : 0 ) -
|
||||
( ( lptr->flag & LVAR_INCASE ) ? 1 : 0 ) -
|
||||
( ( lptr->flag & LVAR_ANYEND ) ? 1 : 0 );
|
||||
if ( lptr->len > 255 )
|
||||
elog(ERROR,"Name of level is too long (%d, must be < 256) in position %d",
|
||||
lptr->len, (int)(lptr->start - buf));
|
||||
}
|
||||
else if (*ptr == '|')
|
||||
{
|
||||
lptr->len = ptr - lptr->start -
|
||||
((lptr->flag & LVAR_SUBLEXEM) ? 1 : 0) -
|
||||
((lptr->flag & LVAR_INCASE) ? 1 : 0) -
|
||||
((lptr->flag & LVAR_ANYEND) ? 1 : 0);
|
||||
if (lptr->len > 255)
|
||||
elog(ERROR, "Name of level is too long (%d, must be < 256) in position %d",
|
||||
lptr->len, (int) (lptr->start - buf));
|
||||
state = LQPRS_WAITVAR;
|
||||
} else if ( *ptr == '.' ) {
|
||||
lptr->len = ptr - lptr->start -
|
||||
( ( lptr->flag & LVAR_SUBLEXEM ) ? 1 : 0 ) -
|
||||
( ( lptr->flag & LVAR_INCASE ) ? 1 : 0 ) -
|
||||
( ( lptr->flag & LVAR_ANYEND ) ? 1 : 0 );
|
||||
if ( lptr->len > 255 )
|
||||
elog(ERROR,"Name of level is too long (%d, must be < 256) in position %d",
|
||||
lptr->len, (int)(lptr->start - buf));
|
||||
}
|
||||
else if (*ptr == '.')
|
||||
{
|
||||
lptr->len = ptr - lptr->start -
|
||||
((lptr->flag & LVAR_SUBLEXEM) ? 1 : 0) -
|
||||
((lptr->flag & LVAR_INCASE) ? 1 : 0) -
|
||||
((lptr->flag & LVAR_ANYEND) ? 1 : 0);
|
||||
if (lptr->len > 255)
|
||||
elog(ERROR, "Name of level is too long (%d, must be < 256) in position %d",
|
||||
lptr->len, (int) (lptr->start - buf));
|
||||
state = LQPRS_WAITLEVEL;
|
||||
curqlevel = NEXTLEV(curqlevel);
|
||||
} else if ( ISALNUM(*ptr) ) {
|
||||
if ( lptr->flag )
|
||||
}
|
||||
else if (ISALNUM(*ptr))
|
||||
{
|
||||
if (lptr->flag)
|
||||
UNCHAR;
|
||||
} else
|
||||
}
|
||||
else
|
||||
UNCHAR;
|
||||
} else if ( state == LQPRS_WAITOPEN ) {
|
||||
if ( *ptr == '{' ) {
|
||||
}
|
||||
else if (state == LQPRS_WAITOPEN)
|
||||
{
|
||||
if (*ptr == '{')
|
||||
state = LQPRS_WAITFNUM;
|
||||
} else if ( *ptr == '.' ) {
|
||||
curqlevel->low=0;
|
||||
curqlevel->high=0xffff;
|
||||
else if (*ptr == '.')
|
||||
{
|
||||
curqlevel->low = 0;
|
||||
curqlevel->high = 0xffff;
|
||||
curqlevel = NEXTLEV(curqlevel);
|
||||
state = LQPRS_WAITLEVEL;
|
||||
} else
|
||||
}
|
||||
else
|
||||
UNCHAR;
|
||||
} else if ( state == LQPRS_WAITFNUM ) {
|
||||
if ( *ptr == ',' ) {
|
||||
state = LQPRS_WAITSNUM;
|
||||
} else if ( isdigit((unsigned int)*ptr) ) {
|
||||
curqlevel->low = atoi( ptr );
|
||||
}
|
||||
else if (state == LQPRS_WAITFNUM)
|
||||
{
|
||||
if (*ptr == ',')
|
||||
state = LQPRS_WAITSNUM;
|
||||
else if (isdigit((unsigned int) *ptr))
|
||||
{
|
||||
curqlevel->low = atoi(ptr);
|
||||
state = LQPRS_WAITND;
|
||||
} else
|
||||
UNCHAR;
|
||||
} else if ( state == LQPRS_WAITSNUM ) {
|
||||
if ( isdigit((unsigned int)*ptr) ) {
|
||||
curqlevel->high = atoi( ptr );
|
||||
state = LQPRS_WAITCLOSE;
|
||||
} else if ( *ptr == '}' ) {
|
||||
}
|
||||
else
|
||||
UNCHAR;
|
||||
}
|
||||
else if (state == LQPRS_WAITSNUM)
|
||||
{
|
||||
if (isdigit((unsigned int) *ptr))
|
||||
{
|
||||
curqlevel->high = atoi(ptr);
|
||||
state = LQPRS_WAITCLOSE;
|
||||
}
|
||||
else if (*ptr == '}')
|
||||
{
|
||||
curqlevel->high = 0xffff;
|
||||
state = LQPRS_WAITEND;
|
||||
} else
|
||||
}
|
||||
else
|
||||
UNCHAR;
|
||||
} else if ( state == LQPRS_WAITCLOSE ) {
|
||||
if ( *ptr == '}' )
|
||||
}
|
||||
else if (state == LQPRS_WAITCLOSE)
|
||||
{
|
||||
if (*ptr == '}')
|
||||
state = LQPRS_WAITEND;
|
||||
else if ( !isdigit((unsigned int)*ptr) )
|
||||
else if (!isdigit((unsigned int) *ptr))
|
||||
UNCHAR;
|
||||
} else if ( state == LQPRS_WAITND ) {
|
||||
if ( *ptr == '}' ) {
|
||||
}
|
||||
else if (state == LQPRS_WAITND)
|
||||
{
|
||||
if (*ptr == '}')
|
||||
{
|
||||
curqlevel->high = curqlevel->low;
|
||||
state = LQPRS_WAITEND;
|
||||
} else if ( *ptr == ',' )
|
||||
}
|
||||
else if (*ptr == ',')
|
||||
state = LQPRS_WAITSNUM;
|
||||
else if ( !isdigit((unsigned int)*ptr) )
|
||||
else if (!isdigit((unsigned int) *ptr))
|
||||
UNCHAR;
|
||||
} else if ( state == LQPRS_WAITEND ) {
|
||||
if ( *ptr == '.' ) {
|
||||
}
|
||||
else if (state == LQPRS_WAITEND)
|
||||
{
|
||||
if (*ptr == '.')
|
||||
{
|
||||
state = LQPRS_WAITLEVEL;
|
||||
curqlevel = NEXTLEV(curqlevel);
|
||||
} else
|
||||
}
|
||||
else
|
||||
UNCHAR;
|
||||
} else
|
||||
elog(ERROR,"Inner error in parser");
|
||||
}
|
||||
else
|
||||
elog(ERROR, "Inner error in parser");
|
||||
ptr++;
|
||||
}
|
||||
|
||||
if ( state==LQPRS_WAITDELIM ) {
|
||||
if ( lptr->start == ptr )
|
||||
elog(ERROR,"Unexpected end of line");
|
||||
|
||||
if (state == LQPRS_WAITDELIM)
|
||||
{
|
||||
if (lptr->start == ptr)
|
||||
elog(ERROR, "Unexpected end of line");
|
||||
lptr->len = ptr - lptr->start -
|
||||
( ( lptr->flag & LVAR_SUBLEXEM ) ? 1 : 0 ) -
|
||||
( ( lptr->flag & LVAR_INCASE ) ? 1 : 0 ) -
|
||||
( ( lptr->flag & LVAR_ANYEND ) ? 1 : 0 );
|
||||
if ( lptr->len==0 )
|
||||
elog(ERROR,"Unexpected end of line");
|
||||
if ( lptr->len > 255 )
|
||||
elog(ERROR,"Name of level is too long (%d, must be < 256) in position %d",
|
||||
lptr->len, (int)(lptr->start - buf));
|
||||
} else if ( state == LQPRS_WAITOPEN ) {
|
||||
((lptr->flag & LVAR_SUBLEXEM) ? 1 : 0) -
|
||||
((lptr->flag & LVAR_INCASE) ? 1 : 0) -
|
||||
((lptr->flag & LVAR_ANYEND) ? 1 : 0);
|
||||
if (lptr->len == 0)
|
||||
elog(ERROR, "Unexpected end of line");
|
||||
if (lptr->len > 255)
|
||||
elog(ERROR, "Name of level is too long (%d, must be < 256) in position %d",
|
||||
lptr->len, (int) (lptr->start - buf));
|
||||
}
|
||||
else if (state == LQPRS_WAITOPEN)
|
||||
curqlevel->high = 0xffff;
|
||||
} else if ( state != LQPRS_WAITEND )
|
||||
elog(ERROR,"Unexpected end of line");
|
||||
|
||||
else if (state != LQPRS_WAITEND)
|
||||
elog(ERROR, "Unexpected end of line");
|
||||
|
||||
curqlevel = tmpql;
|
||||
totallen = LQUERY_HDRSIZE;
|
||||
while( (char*)curqlevel-(char*)tmpql < num*ITEMSIZE ) {
|
||||
totallen += LQL_HDRSIZE;
|
||||
if ( curqlevel->numvar ) {
|
||||
totallen = LQUERY_HDRSIZE;
|
||||
while ((char *) curqlevel - (char *) tmpql < num * ITEMSIZE)
|
||||
{
|
||||
totallen += LQL_HDRSIZE;
|
||||
if (curqlevel->numvar)
|
||||
{
|
||||
lptr = GETVAR(curqlevel);
|
||||
while( lptr-GETVAR(curqlevel) < curqlevel->numvar ) {
|
||||
while (lptr - GETVAR(curqlevel) < curqlevel->numvar)
|
||||
{
|
||||
totallen += MAXALIGN(LVAR_HDRSIZE + lptr->len);
|
||||
lptr++;
|
||||
}
|
||||
} else if ( curqlevel->low > curqlevel->high )
|
||||
elog(ERROR,"Low limit(%d) is greater than upper(%d)",curqlevel->low,curqlevel->high );
|
||||
}
|
||||
else if (curqlevel->low > curqlevel->high)
|
||||
elog(ERROR, "Low limit(%d) is greater than upper(%d)", curqlevel->low, curqlevel->high);
|
||||
curqlevel = NEXTLEV(curqlevel);
|
||||
}
|
||||
|
||||
result = (lquery*)palloc( totallen );
|
||||
result = (lquery *) palloc(totallen);
|
||||
result->len = totallen;
|
||||
result->numlevel = num;
|
||||
result->firstgood = 0;
|
||||
result->flag=0;
|
||||
if ( hasnot )
|
||||
result->flag = 0;
|
||||
if (hasnot)
|
||||
result->flag |= LQUERY_HASNOT;
|
||||
cur = LQUERY_FIRST(result);
|
||||
curqlevel = tmpql;
|
||||
while( (char*)curqlevel-(char*)tmpql < num*ITEMSIZE ) {
|
||||
memcpy(cur,curqlevel,LQL_HDRSIZE);
|
||||
cur->totallen=LQL_HDRSIZE;
|
||||
if ( curqlevel->numvar ) {
|
||||
while ((char *) curqlevel - (char *) tmpql < num * ITEMSIZE)
|
||||
{
|
||||
memcpy(cur, curqlevel, LQL_HDRSIZE);
|
||||
cur->totallen = LQL_HDRSIZE;
|
||||
if (curqlevel->numvar)
|
||||
{
|
||||
lrptr = LQL_FIRST(cur);
|
||||
lptr = GETVAR(curqlevel);
|
||||
while( lptr-GETVAR(curqlevel) < curqlevel->numvar ) {
|
||||
while (lptr - GETVAR(curqlevel) < curqlevel->numvar)
|
||||
{
|
||||
cur->totallen += MAXALIGN(LVAR_HDRSIZE + lptr->len);
|
||||
lrptr->len = lptr->len;
|
||||
lrptr->len = lptr->len;
|
||||
lrptr->flag = lptr->flag;
|
||||
lrptr->val = ltree_crc32_sz((uint8 *) lptr->start, lptr->len);
|
||||
memcpy( lrptr->name, lptr->start, lptr->len);
|
||||
memcpy(lrptr->name, lptr->start, lptr->len);
|
||||
lptr++;
|
||||
lrptr = LVAR_NEXT( lrptr );
|
||||
lrptr = LVAR_NEXT(lrptr);
|
||||
}
|
||||
pfree( GETVAR(curqlevel) );
|
||||
if ( cur->numvar > 1 || cur->flag != 0 )
|
||||
wasbad=true;
|
||||
else if ( wasbad==false )
|
||||
(result->firstgood)++;
|
||||
} else
|
||||
wasbad=true;
|
||||
pfree(GETVAR(curqlevel));
|
||||
if (cur->numvar > 1 || cur->flag != 0)
|
||||
wasbad = true;
|
||||
else if (wasbad == false)
|
||||
(result->firstgood)++;
|
||||
}
|
||||
else
|
||||
wasbad = true;
|
||||
curqlevel = NEXTLEV(curqlevel);
|
||||
cur = LQL_NEXT(cur);
|
||||
}
|
||||
@@ -353,82 +441,104 @@ lquery_in(PG_FUNCTION_ARGS) {
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
Datum
|
||||
lquery_out(PG_FUNCTION_ARGS) {
|
||||
lquery *in = PG_GETARG_LQUERY(0);
|
||||
char *buf,*ptr;
|
||||
int i,j,totallen=0;
|
||||
lquery_level *curqlevel;
|
||||
lquery_variant *curtlevel;
|
||||
Datum
|
||||
lquery_out(PG_FUNCTION_ARGS)
|
||||
{
|
||||
lquery *in = PG_GETARG_LQUERY(0);
|
||||
char *buf,
|
||||
*ptr;
|
||||
int i,
|
||||
j,
|
||||
totallen = 0;
|
||||
lquery_level *curqlevel;
|
||||
lquery_variant *curtlevel;
|
||||
|
||||
curqlevel = LQUERY_FIRST(in);
|
||||
for(i=0;i<in->numlevel;i++) {
|
||||
if ( curqlevel->numvar )
|
||||
totallen = (curqlevel->numvar*4) + 1 + curqlevel->totallen;
|
||||
for (i = 0; i < in->numlevel; i++)
|
||||
{
|
||||
if (curqlevel->numvar)
|
||||
totallen = (curqlevel->numvar * 4) + 1 + curqlevel->totallen;
|
||||
else
|
||||
totallen = 2*11 + 4;
|
||||
totallen = 2 * 11 + 4;
|
||||
totallen++;
|
||||
curqlevel = LQL_NEXT(curqlevel);
|
||||
}
|
||||
|
||||
|
||||
ptr = buf = (char*)palloc( totallen );
|
||||
|
||||
|
||||
ptr = buf = (char *) palloc(totallen);
|
||||
curqlevel = LQUERY_FIRST(in);
|
||||
for(i=0;i<in->numlevel;i++) {
|
||||
if ( i!=0 ) {
|
||||
for (i = 0; i < in->numlevel; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
*ptr = '.';
|
||||
ptr++;
|
||||
}
|
||||
if ( curqlevel->numvar ) {
|
||||
if ( curqlevel->flag & LQL_NOT ) {
|
||||
if (curqlevel->numvar)
|
||||
{
|
||||
if (curqlevel->flag & LQL_NOT)
|
||||
{
|
||||
*ptr = '!';
|
||||
ptr++;
|
||||
}
|
||||
curtlevel = LQL_FIRST(curqlevel);
|
||||
for(j=0;j<curqlevel->numvar;j++) {
|
||||
if ( j!=0 ) {
|
||||
for (j = 0; j < curqlevel->numvar; j++)
|
||||
{
|
||||
if (j != 0)
|
||||
{
|
||||
*ptr = '|';
|
||||
ptr++;
|
||||
}
|
||||
memcpy( ptr, curtlevel->name, curtlevel->len );
|
||||
ptr+=curtlevel->len;
|
||||
if ( (curtlevel->flag & LVAR_SUBLEXEM) ) {
|
||||
memcpy(ptr, curtlevel->name, curtlevel->len);
|
||||
ptr += curtlevel->len;
|
||||
if ((curtlevel->flag & LVAR_SUBLEXEM))
|
||||
{
|
||||
*ptr = '%';
|
||||
ptr++;
|
||||
}
|
||||
if ( (curtlevel->flag & LVAR_INCASE) ) {
|
||||
if ((curtlevel->flag & LVAR_INCASE))
|
||||
{
|
||||
*ptr = '@';
|
||||
ptr++;
|
||||
}
|
||||
if ( (curtlevel->flag & LVAR_ANYEND) ) {
|
||||
if ((curtlevel->flag & LVAR_ANYEND))
|
||||
{
|
||||
*ptr = '*';
|
||||
ptr++;
|
||||
}
|
||||
curtlevel = LVAR_NEXT(curtlevel);
|
||||
}
|
||||
} else {
|
||||
if ( curqlevel->low == curqlevel->high ) {
|
||||
sprintf(ptr,"*{%d}",curqlevel->low);
|
||||
} else if ( curqlevel->low == 0 ) {
|
||||
if ( curqlevel->high == 0xffff ) {
|
||||
*ptr='*';
|
||||
*(ptr+1)='\0';
|
||||
} else
|
||||
sprintf(ptr,"*{,%d}",curqlevel->high);
|
||||
} else if ( curqlevel->high == 0xffff ) {
|
||||
sprintf(ptr,"*{%d,}",curqlevel->low);
|
||||
} else
|
||||
sprintf(ptr,"*{%d,%d}", curqlevel->low, curqlevel->high);
|
||||
ptr = strchr(ptr,'\0');
|
||||
}
|
||||
else
|
||||
{
|
||||
if (curqlevel->low == curqlevel->high)
|
||||
{
|
||||
sprintf(ptr, "*{%d}", curqlevel->low);
|
||||
}
|
||||
else if (curqlevel->low == 0)
|
||||
{
|
||||
if (curqlevel->high == 0xffff)
|
||||
{
|
||||
*ptr = '*';
|
||||
*(ptr + 1) = '\0';
|
||||
}
|
||||
else
|
||||
sprintf(ptr, "*{,%d}", curqlevel->high);
|
||||
}
|
||||
else if (curqlevel->high == 0xffff)
|
||||
{
|
||||
sprintf(ptr, "*{%d,}", curqlevel->low);
|
||||
}
|
||||
else
|
||||
sprintf(ptr, "*{%d,%d}", curqlevel->low, curqlevel->high);
|
||||
ptr = strchr(ptr, '\0');
|
||||
}
|
||||
|
||||
curqlevel = LQL_NEXT(curqlevel);
|
||||
}
|
||||
|
||||
*ptr='\0';
|
||||
PG_FREE_IF_COPY(in,0);
|
||||
*ptr = '\0';
|
||||
PG_FREE_IF_COPY(in, 0);
|
||||
|
||||
PG_RETURN_POINTER(buf);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* op function for ltree
|
||||
* op function for ltree
|
||||
* Teodor Sigaev <teodor@stack.net>
|
||||
*/
|
||||
|
||||
@@ -23,45 +23,50 @@ PG_FUNCTION_INFO_V1(ltree_addltree);
|
||||
PG_FUNCTION_INFO_V1(ltree_addtext);
|
||||
PG_FUNCTION_INFO_V1(ltree_textadd);
|
||||
PG_FUNCTION_INFO_V1(lca);
|
||||
Datum ltree_cmp(PG_FUNCTION_ARGS);
|
||||
Datum ltree_lt(PG_FUNCTION_ARGS);
|
||||
Datum ltree_le(PG_FUNCTION_ARGS);
|
||||
Datum ltree_eq(PG_FUNCTION_ARGS);
|
||||
Datum ltree_ne(PG_FUNCTION_ARGS);
|
||||
Datum ltree_ge(PG_FUNCTION_ARGS);
|
||||
Datum ltree_gt(PG_FUNCTION_ARGS);
|
||||
Datum nlevel(PG_FUNCTION_ARGS);
|
||||
Datum subltree(PG_FUNCTION_ARGS);
|
||||
Datum subpath(PG_FUNCTION_ARGS);
|
||||
Datum ltree_addltree(PG_FUNCTION_ARGS);
|
||||
Datum ltree_addtext(PG_FUNCTION_ARGS);
|
||||
Datum ltree_textadd(PG_FUNCTION_ARGS);
|
||||
Datum lca(PG_FUNCTION_ARGS);
|
||||
Datum ltree_cmp(PG_FUNCTION_ARGS);
|
||||
Datum ltree_lt(PG_FUNCTION_ARGS);
|
||||
Datum ltree_le(PG_FUNCTION_ARGS);
|
||||
Datum ltree_eq(PG_FUNCTION_ARGS);
|
||||
Datum ltree_ne(PG_FUNCTION_ARGS);
|
||||
Datum ltree_ge(PG_FUNCTION_ARGS);
|
||||
Datum ltree_gt(PG_FUNCTION_ARGS);
|
||||
Datum nlevel(PG_FUNCTION_ARGS);
|
||||
Datum subltree(PG_FUNCTION_ARGS);
|
||||
Datum subpath(PG_FUNCTION_ARGS);
|
||||
Datum ltree_addltree(PG_FUNCTION_ARGS);
|
||||
Datum ltree_addtext(PG_FUNCTION_ARGS);
|
||||
Datum ltree_textadd(PG_FUNCTION_ARGS);
|
||||
Datum lca(PG_FUNCTION_ARGS);
|
||||
|
||||
int
|
||||
ltree_compare(const ltree *a, const ltree *b) {
|
||||
ltree_compare(const ltree * a, const ltree * b)
|
||||
{
|
||||
ltree_level *al = LTREE_FIRST(a);
|
||||
ltree_level *bl = LTREE_FIRST(b);
|
||||
int an = a->numlevel;
|
||||
int bn = b->numlevel;
|
||||
int res = 0;
|
||||
int an = a->numlevel;
|
||||
int bn = b->numlevel;
|
||||
int res = 0;
|
||||
|
||||
while( an>0 && bn>0 ) {
|
||||
if ( (res = strncmp( al->name, bl->name, min(al->len, bl->len))) == 0 ) {
|
||||
if ( al->len != bl->len )
|
||||
return (al->len - bl->len)*10*(an+1);
|
||||
} else
|
||||
return res*10*(an+1);
|
||||
while (an > 0 && bn > 0)
|
||||
{
|
||||
if ((res = strncmp(al->name, bl->name, min(al->len, bl->len))) == 0)
|
||||
{
|
||||
if (al->len != bl->len)
|
||||
return (al->len - bl->len) * 10 * (an + 1);
|
||||
}
|
||||
else
|
||||
return res * 10 * (an + 1);
|
||||
|
||||
an--; bn--;
|
||||
al = LEVEL_NEXT(al);
|
||||
bl = LEVEL_NEXT(bl);
|
||||
an--;
|
||||
bn--;
|
||||
al = LEVEL_NEXT(al);
|
||||
bl = LEVEL_NEXT(bl);
|
||||
}
|
||||
|
||||
return (a->numlevel - b->numlevel)*10*(an+1);
|
||||
}
|
||||
return (a->numlevel - b->numlevel) * 10 * (an + 1);
|
||||
}
|
||||
|
||||
#define RUNCMP \
|
||||
#define RUNCMP \
|
||||
ltree *a = PG_GETARG_LTREE(0); \
|
||||
ltree *b = PG_GETARG_LTREE(1); \
|
||||
int res = ltree_compare(a,b); \
|
||||
@@ -69,320 +74,360 @@ PG_FREE_IF_COPY(a,0); \
|
||||
PG_FREE_IF_COPY(b,1); \
|
||||
|
||||
Datum
|
||||
ltree_cmp(PG_FUNCTION_ARGS) {
|
||||
ltree_cmp(PG_FUNCTION_ARGS)
|
||||
{
|
||||
RUNCMP
|
||||
PG_RETURN_INT32(res);
|
||||
}
|
||||
|
||||
Datum
|
||||
ltree_lt(PG_FUNCTION_ARGS) {
|
||||
ltree_lt(PG_FUNCTION_ARGS)
|
||||
{
|
||||
RUNCMP
|
||||
PG_RETURN_BOOL( (res<0) ? true : false );
|
||||
PG_RETURN_BOOL((res < 0) ? true : false);
|
||||
}
|
||||
|
||||
Datum
|
||||
ltree_le(PG_FUNCTION_ARGS) {
|
||||
ltree_le(PG_FUNCTION_ARGS)
|
||||
{
|
||||
RUNCMP
|
||||
PG_RETURN_BOOL( (res<=0) ? true : false );
|
||||
PG_RETURN_BOOL((res <= 0) ? true : false);
|
||||
}
|
||||
|
||||
Datum
|
||||
ltree_eq(PG_FUNCTION_ARGS) {
|
||||
ltree_eq(PG_FUNCTION_ARGS)
|
||||
{
|
||||
RUNCMP
|
||||
PG_RETURN_BOOL( (res==0) ? true : false );
|
||||
PG_RETURN_BOOL((res == 0) ? true : false);
|
||||
}
|
||||
|
||||
Datum
|
||||
ltree_ge(PG_FUNCTION_ARGS) {
|
||||
ltree_ge(PG_FUNCTION_ARGS)
|
||||
{
|
||||
RUNCMP
|
||||
PG_RETURN_BOOL( (res>=0) ? true : false );
|
||||
PG_RETURN_BOOL((res >= 0) ? true : false);
|
||||
}
|
||||
|
||||
Datum
|
||||
ltree_gt(PG_FUNCTION_ARGS) {
|
||||
ltree_gt(PG_FUNCTION_ARGS)
|
||||
{
|
||||
RUNCMP
|
||||
PG_RETURN_BOOL( (res>0) ? true : false );
|
||||
PG_RETURN_BOOL((res > 0) ? true : false);
|
||||
}
|
||||
|
||||
Datum
|
||||
ltree_ne(PG_FUNCTION_ARGS) {
|
||||
ltree_ne(PG_FUNCTION_ARGS)
|
||||
{
|
||||
RUNCMP
|
||||
PG_RETURN_BOOL( (res!=0) ? true : false );
|
||||
PG_RETURN_BOOL((res != 0) ? true : false);
|
||||
}
|
||||
|
||||
Datum
|
||||
nlevel(PG_FUNCTION_ARGS) {
|
||||
ltree *a = PG_GETARG_LTREE(0);
|
||||
int res = a->numlevel;
|
||||
PG_FREE_IF_COPY(a,0);
|
||||
nlevel(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ltree *a = PG_GETARG_LTREE(0);
|
||||
int res = a->numlevel;
|
||||
|
||||
PG_FREE_IF_COPY(a, 0);
|
||||
PG_RETURN_INT32(res);
|
||||
}
|
||||
|
||||
bool
|
||||
inner_isparent(const ltree *c, const ltree *p) {
|
||||
inner_isparent(const ltree * c, const ltree * p)
|
||||
{
|
||||
ltree_level *cl = LTREE_FIRST(c);
|
||||
ltree_level *pl = LTREE_FIRST(p);
|
||||
int pn = p->numlevel;
|
||||
int pn = p->numlevel;
|
||||
|
||||
if ( pn > c->numlevel )
|
||||
if (pn > c->numlevel)
|
||||
return false;
|
||||
|
||||
while( pn>0 ) {
|
||||
if ( cl->len != pl->len )
|
||||
while (pn > 0)
|
||||
{
|
||||
if (cl->len != pl->len)
|
||||
return false;
|
||||
if ( strncmp( cl->name, pl->name, cl->len ) )
|
||||
if (strncmp(cl->name, pl->name, cl->len))
|
||||
return false;
|
||||
|
||||
pn--;
|
||||
cl = LEVEL_NEXT(cl);
|
||||
pl = LEVEL_NEXT(pl);
|
||||
cl = LEVEL_NEXT(cl);
|
||||
pl = LEVEL_NEXT(pl);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Datum
|
||||
ltree_isparent(PG_FUNCTION_ARGS) {
|
||||
ltree *c = PG_GETARG_LTREE(1);
|
||||
ltree *p = PG_GETARG_LTREE(0);
|
||||
bool res = inner_isparent(c,p);
|
||||
PG_FREE_IF_COPY(c,1);
|
||||
PG_FREE_IF_COPY(p,0);
|
||||
PG_RETURN_BOOL( res );
|
||||
Datum
|
||||
ltree_isparent(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ltree *c = PG_GETARG_LTREE(1);
|
||||
ltree *p = PG_GETARG_LTREE(0);
|
||||
bool res = inner_isparent(c, p);
|
||||
|
||||
PG_FREE_IF_COPY(c, 1);
|
||||
PG_FREE_IF_COPY(p, 0);
|
||||
PG_RETURN_BOOL(res);
|
||||
}
|
||||
|
||||
Datum
|
||||
ltree_risparent(PG_FUNCTION_ARGS) {
|
||||
ltree *c = PG_GETARG_LTREE(0);
|
||||
ltree *p = PG_GETARG_LTREE(1);
|
||||
bool res = inner_isparent(c,p);
|
||||
PG_FREE_IF_COPY(c,0);
|
||||
PG_FREE_IF_COPY(p,1);
|
||||
PG_RETURN_BOOL( res );
|
||||
Datum
|
||||
ltree_risparent(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ltree *c = PG_GETARG_LTREE(0);
|
||||
ltree *p = PG_GETARG_LTREE(1);
|
||||
bool res = inner_isparent(c, p);
|
||||
|
||||
PG_FREE_IF_COPY(c, 0);
|
||||
PG_FREE_IF_COPY(p, 1);
|
||||
PG_RETURN_BOOL(res);
|
||||
}
|
||||
|
||||
|
||||
static ltree*
|
||||
inner_subltree(ltree *t, int4 startpos, int4 endpos) {
|
||||
char *start=NULL,*end=NULL;
|
||||
static ltree *
|
||||
inner_subltree(ltree * t, int4 startpos, int4 endpos)
|
||||
{
|
||||
char *start = NULL,
|
||||
*end = NULL;
|
||||
ltree_level *ptr = LTREE_FIRST(t);
|
||||
ltree *res;
|
||||
int i;
|
||||
ltree *res;
|
||||
int i;
|
||||
|
||||
if ( startpos <0 || endpos <0 || startpos>=t->numlevel || startpos >= endpos )
|
||||
elog(ERROR,"Wrong positions");
|
||||
if (startpos < 0 || endpos < 0 || startpos >= t->numlevel || startpos >= endpos)
|
||||
elog(ERROR, "Wrong positions");
|
||||
|
||||
if ( endpos > t->numlevel )
|
||||
if (endpos > t->numlevel)
|
||||
endpos = t->numlevel;
|
||||
|
||||
for(i=0;i<endpos ;i++) {
|
||||
if ( i==startpos )
|
||||
start = (char*)ptr;
|
||||
if ( i==endpos-1 ) {
|
||||
end = (char*)LEVEL_NEXT(ptr);
|
||||
for (i = 0; i < endpos; i++)
|
||||
{
|
||||
if (i == startpos)
|
||||
start = (char *) ptr;
|
||||
if (i == endpos - 1)
|
||||
{
|
||||
end = (char *) LEVEL_NEXT(ptr);
|
||||
break;
|
||||
}
|
||||
ptr = LEVEL_NEXT(ptr);
|
||||
ptr = LEVEL_NEXT(ptr);
|
||||
}
|
||||
|
||||
res=(ltree*)palloc( LTREE_HDRSIZE + (end-start) );
|
||||
res->len = LTREE_HDRSIZE + (end-start);
|
||||
res->numlevel = endpos-startpos;
|
||||
res = (ltree *) palloc(LTREE_HDRSIZE + (end - start));
|
||||
res->len = LTREE_HDRSIZE + (end - start);
|
||||
res->numlevel = endpos - startpos;
|
||||
|
||||
memcpy(LTREE_FIRST(res), start, end - start);
|
||||
|
||||
memcpy( LTREE_FIRST(res), start, end-start);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
Datum
|
||||
subltree(PG_FUNCTION_ARGS) {
|
||||
ltree *t = PG_GETARG_LTREE(0);
|
||||
ltree *res = inner_subltree(t,PG_GETARG_INT32(1),PG_GETARG_INT32(2));
|
||||
subltree(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ltree *t = PG_GETARG_LTREE(0);
|
||||
ltree *res = inner_subltree(t, PG_GETARG_INT32(1), PG_GETARG_INT32(2));
|
||||
|
||||
PG_FREE_IF_COPY(t,0);
|
||||
PG_FREE_IF_COPY(t, 0);
|
||||
PG_RETURN_POINTER(res);
|
||||
}
|
||||
|
||||
Datum
|
||||
subpath(PG_FUNCTION_ARGS) {
|
||||
ltree *t = PG_GETARG_LTREE(0);
|
||||
int4 start = PG_GETARG_INT32(1);
|
||||
int4 len = ( fcinfo->nargs==3 ) ? PG_GETARG_INT32(2) : 0;
|
||||
int4 end;
|
||||
ltree *res;
|
||||
Datum
|
||||
subpath(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ltree *t = PG_GETARG_LTREE(0);
|
||||
int4 start = PG_GETARG_INT32(1);
|
||||
int4 len = (fcinfo->nargs == 3) ? PG_GETARG_INT32(2) : 0;
|
||||
int4 end;
|
||||
ltree *res;
|
||||
|
||||
end = start+len;
|
||||
end = start + len;
|
||||
|
||||
if ( start < 0 ) {
|
||||
if (start < 0)
|
||||
{
|
||||
start = t->numlevel + start;
|
||||
end = start+len;
|
||||
end = start + len;
|
||||
}
|
||||
if ( start < 0 ) { /* start > t->numlevel */
|
||||
if (start < 0)
|
||||
{ /* start > t->numlevel */
|
||||
start = t->numlevel + start;
|
||||
end = start+len;
|
||||
end = start + len;
|
||||
}
|
||||
|
||||
if ( len < 0 )
|
||||
if (len < 0)
|
||||
end = t->numlevel + len;
|
||||
else if ( len == 0 )
|
||||
else if (len == 0)
|
||||
end = 0xffff;
|
||||
|
||||
res = inner_subltree(t,start,end);
|
||||
res = inner_subltree(t, start, end);
|
||||
|
||||
PG_FREE_IF_COPY(t,0);
|
||||
PG_FREE_IF_COPY(t, 0);
|
||||
PG_RETURN_POINTER(res);
|
||||
}
|
||||
|
||||
static ltree*
|
||||
ltree_concat( ltree *a, ltree *b) {
|
||||
ltree *r;
|
||||
r=(ltree*)palloc( a->len + b->len - LTREE_HDRSIZE);
|
||||
r->len = a->len + b->len - LTREE_HDRSIZE;
|
||||
r->numlevel = a->numlevel + b->numlevel;
|
||||
|
||||
memcpy( LTREE_FIRST(r), LTREE_FIRST(a), a->len - LTREE_HDRSIZE);
|
||||
memcpy( ((char*)LTREE_FIRST(r))+ a->len - LTREE_HDRSIZE, LTREE_FIRST(b), b->len -
|
||||
LTREE_HDRSIZE);
|
||||
return r;
|
||||
}
|
||||
|
||||
Datum
|
||||
ltree_addltree(PG_FUNCTION_ARGS) {
|
||||
ltree *a = PG_GETARG_LTREE(0);
|
||||
ltree *b = PG_GETARG_LTREE(1);
|
||||
ltree *r;
|
||||
static ltree *
|
||||
ltree_concat(ltree * a, ltree * b)
|
||||
{
|
||||
ltree *r;
|
||||
|
||||
r = (ltree *) palloc(a->len + b->len - LTREE_HDRSIZE);
|
||||
r->len = a->len + b->len - LTREE_HDRSIZE;
|
||||
r->numlevel = a->numlevel + b->numlevel;
|
||||
|
||||
memcpy(LTREE_FIRST(r), LTREE_FIRST(a), a->len - LTREE_HDRSIZE);
|
||||
memcpy(((char *) LTREE_FIRST(r)) + a->len - LTREE_HDRSIZE, LTREE_FIRST(b), b->len -
|
||||
LTREE_HDRSIZE);
|
||||
return r;
|
||||
}
|
||||
|
||||
Datum
|
||||
ltree_addltree(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ltree *a = PG_GETARG_LTREE(0);
|
||||
ltree *b = PG_GETARG_LTREE(1);
|
||||
ltree *r;
|
||||
|
||||
r = ltree_concat(a, b);
|
||||
PG_FREE_IF_COPY(a,0);
|
||||
PG_FREE_IF_COPY(b,1);
|
||||
PG_FREE_IF_COPY(a, 0);
|
||||
PG_FREE_IF_COPY(b, 1);
|
||||
PG_RETURN_POINTER(r);
|
||||
}
|
||||
|
||||
Datum
|
||||
ltree_addtext(PG_FUNCTION_ARGS) {
|
||||
ltree *a = PG_GETARG_LTREE(0);
|
||||
text *b = PG_GETARG_TEXT_P(1);
|
||||
char *s;
|
||||
ltree *r,*tmp;
|
||||
ltree_addtext(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ltree *a = PG_GETARG_LTREE(0);
|
||||
text *b = PG_GETARG_TEXT_P(1);
|
||||
char *s;
|
||||
ltree *r,
|
||||
*tmp;
|
||||
|
||||
s = (char*)palloc( VARSIZE(b) - VARHDRSZ+1 );
|
||||
memcpy(s, VARDATA(b), VARSIZE(b) - VARHDRSZ );
|
||||
s = (char *) palloc(VARSIZE(b) - VARHDRSZ + 1);
|
||||
memcpy(s, VARDATA(b), VARSIZE(b) - VARHDRSZ);
|
||||
s[VARSIZE(b) - VARHDRSZ] = '\0';
|
||||
|
||||
tmp = (ltree*)DatumGetPointer( DirectFunctionCall1(
|
||||
ltree_in,
|
||||
PointerGetDatum(s)
|
||||
) );
|
||||
tmp = (ltree *) DatumGetPointer(DirectFunctionCall1(
|
||||
ltree_in,
|
||||
PointerGetDatum(s)
|
||||
));
|
||||
|
||||
pfree(s);
|
||||
|
||||
r = ltree_concat(a,tmp);
|
||||
r = ltree_concat(a, tmp);
|
||||
|
||||
pfree( tmp );
|
||||
|
||||
PG_FREE_IF_COPY(a,0);
|
||||
PG_FREE_IF_COPY(b,1);
|
||||
pfree(tmp);
|
||||
|
||||
PG_FREE_IF_COPY(a, 0);
|
||||
PG_FREE_IF_COPY(b, 1);
|
||||
PG_RETURN_POINTER(r);
|
||||
}
|
||||
|
||||
Datum
|
||||
ltree_textadd(PG_FUNCTION_ARGS) {
|
||||
ltree *a = PG_GETARG_LTREE(1);
|
||||
text *b = PG_GETARG_TEXT_P(0);
|
||||
char *s;
|
||||
ltree *r,*tmp;
|
||||
ltree_textadd(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ltree *a = PG_GETARG_LTREE(1);
|
||||
text *b = PG_GETARG_TEXT_P(0);
|
||||
char *s;
|
||||
ltree *r,
|
||||
*tmp;
|
||||
|
||||
s = (char*)palloc( VARSIZE(b) - VARHDRSZ + 1 );
|
||||
memcpy(s, VARDATA(b), VARSIZE(b) - VARHDRSZ );
|
||||
s[VARSIZE(b) - VARHDRSZ] = '\0';
|
||||
|
||||
tmp = (ltree*)DatumGetPointer( DirectFunctionCall1(
|
||||
ltree_in,
|
||||
PointerGetDatum(s)
|
||||
) );
|
||||
s = (char *) palloc(VARSIZE(b) - VARHDRSZ + 1);
|
||||
memcpy(s, VARDATA(b), VARSIZE(b) - VARHDRSZ);
|
||||
s[VARSIZE(b) - VARHDRSZ] = '\0';
|
||||
|
||||
pfree(s);
|
||||
tmp = (ltree *) DatumGetPointer(DirectFunctionCall1(
|
||||
ltree_in,
|
||||
PointerGetDatum(s)
|
||||
));
|
||||
|
||||
r = ltree_concat(tmp,a);
|
||||
pfree(s);
|
||||
|
||||
pfree( tmp );
|
||||
|
||||
PG_FREE_IF_COPY(a,1);
|
||||
PG_FREE_IF_COPY(b,0);
|
||||
PG_RETURN_POINTER(r);
|
||||
r = ltree_concat(tmp, a);
|
||||
|
||||
pfree(tmp);
|
||||
|
||||
PG_FREE_IF_COPY(a, 1);
|
||||
PG_FREE_IF_COPY(b, 0);
|
||||
PG_RETURN_POINTER(r);
|
||||
}
|
||||
|
||||
ltree*
|
||||
lca_inner(ltree** a, int len) {
|
||||
int tmp,num=( (*a)->numlevel ) ? (*a)->numlevel-1 : 0;
|
||||
ltree **ptr=a+1;
|
||||
int i,reslen=LTREE_HDRSIZE;
|
||||
ltree_level *l1, *l2;
|
||||
ltree *res;
|
||||
|
||||
ltree *
|
||||
lca_inner(ltree ** a, int len)
|
||||
{
|
||||
int tmp,
|
||||
num = ((*a)->numlevel) ? (*a)->numlevel - 1 : 0;
|
||||
ltree **ptr = a + 1;
|
||||
int i,
|
||||
reslen = LTREE_HDRSIZE;
|
||||
ltree_level *l1,
|
||||
*l2;
|
||||
ltree *res;
|
||||
|
||||
if ( (*a)->numlevel == 0 )
|
||||
|
||||
if ((*a)->numlevel == 0)
|
||||
return NULL;
|
||||
|
||||
while( ptr-a < len ) {
|
||||
if ( (*ptr)->numlevel == 0 )
|
||||
while (ptr - a < len)
|
||||
{
|
||||
if ((*ptr)->numlevel == 0)
|
||||
return NULL;
|
||||
else if ( (*ptr)->numlevel == 1 )
|
||||
num=0;
|
||||
else {
|
||||
else if ((*ptr)->numlevel == 1)
|
||||
num = 0;
|
||||
else
|
||||
{
|
||||
l1 = LTREE_FIRST(*a);
|
||||
l2 = LTREE_FIRST(*ptr);
|
||||
tmp=num; num=0;
|
||||
for(i=0;i<min(tmp, (*ptr)->numlevel-1); i++) {
|
||||
if ( l1->len == l2->len && strncmp(l1->name,l2->name,l1->len) == 0 )
|
||||
num=i+1;
|
||||
tmp = num;
|
||||
num = 0;
|
||||
for (i = 0; i < min(tmp, (*ptr)->numlevel - 1); i++)
|
||||
{
|
||||
if (l1->len == l2->len && strncmp(l1->name, l2->name, l1->len) == 0)
|
||||
num = i + 1;
|
||||
else
|
||||
break;
|
||||
l1=LEVEL_NEXT(l1);
|
||||
l2=LEVEL_NEXT(l2);
|
||||
l1 = LEVEL_NEXT(l1);
|
||||
l2 = LEVEL_NEXT(l2);
|
||||
}
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
|
||||
l1 = LTREE_FIRST(*a);
|
||||
for(i=0;i<num;i++) {
|
||||
for (i = 0; i < num; i++)
|
||||
{
|
||||
reslen += MAXALIGN(l1->len + LEVEL_HDRSIZE);
|
||||
l1=LEVEL_NEXT(l1);
|
||||
l1 = LEVEL_NEXT(l1);
|
||||
}
|
||||
|
||||
res=(ltree*)palloc( reslen );
|
||||
res = (ltree *) palloc(reslen);
|
||||
res->len = reslen;
|
||||
res->numlevel = num;
|
||||
|
||||
l1 = LTREE_FIRST(*a);
|
||||
l2 = LTREE_FIRST(res);
|
||||
|
||||
for(i=0;i<num;i++) {
|
||||
memcpy(l2,l1, MAXALIGN(l1->len + LEVEL_HDRSIZE));
|
||||
l1=LEVEL_NEXT(l1);
|
||||
l2=LEVEL_NEXT(l2);
|
||||
}
|
||||
for (i = 0; i < num; i++)
|
||||
{
|
||||
memcpy(l2, l1, MAXALIGN(l1->len + LEVEL_HDRSIZE));
|
||||
l1 = LEVEL_NEXT(l1);
|
||||
l2 = LEVEL_NEXT(l2);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
Datum
|
||||
lca(PG_FUNCTION_ARGS) {
|
||||
int i;
|
||||
ltree **a,*res;
|
||||
lca(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int i;
|
||||
ltree **a,
|
||||
*res;
|
||||
|
||||
a=(ltree**)palloc( sizeof(ltree*) * fcinfo->nargs );
|
||||
for(i=0;i<fcinfo->nargs;i++)
|
||||
a = (ltree **) palloc(sizeof(ltree *) * fcinfo->nargs);
|
||||
for (i = 0; i < fcinfo->nargs; i++)
|
||||
a[i] = PG_GETARG_LTREE(i);
|
||||
res = lca_inner(a, (int) fcinfo->nargs);
|
||||
for(i=0;i<fcinfo->nargs;i++)
|
||||
PG_FREE_IF_COPY(a[i],i);
|
||||
res = lca_inner(a, (int) fcinfo->nargs);
|
||||
for (i = 0; i < fcinfo->nargs; i++)
|
||||
PG_FREE_IF_COPY(a[i], i);
|
||||
pfree(a);
|
||||
|
||||
if ( res )
|
||||
|
||||
if (res)
|
||||
PG_RETURN_POINTER(res);
|
||||
else
|
||||
PG_RETURN_NULL();
|
||||
}
|
||||
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* txtquery io
|
||||
* txtquery io
|
||||
* Teodor Sigaev <teodor@stack.net>
|
||||
*/
|
||||
|
||||
@@ -8,44 +8,47 @@
|
||||
#include "crc32.h"
|
||||
|
||||
PG_FUNCTION_INFO_V1(ltxtq_in);
|
||||
Datum ltxtq_in(PG_FUNCTION_ARGS);
|
||||
Datum ltxtq_in(PG_FUNCTION_ARGS);
|
||||
|
||||
PG_FUNCTION_INFO_V1(ltxtq_out);
|
||||
Datum ltxtq_out(PG_FUNCTION_ARGS);
|
||||
Datum ltxtq_out(PG_FUNCTION_ARGS);
|
||||
|
||||
|
||||
/* parser's states */
|
||||
#define WAITOPERAND 1
|
||||
#define INOPERAND 2
|
||||
#define WAITOPERATOR 3
|
||||
#define WAITOPERATOR 3
|
||||
|
||||
/*
|
||||
* node of query tree, also used
|
||||
* for storing polish notation in parser
|
||||
*/
|
||||
typedef struct NODE {
|
||||
int4 type;
|
||||
int4 val;
|
||||
int2 distance;
|
||||
int2 length;
|
||||
uint16 flag;
|
||||
typedef struct NODE
|
||||
{
|
||||
int4 type;
|
||||
int4 val;
|
||||
int2 distance;
|
||||
int2 length;
|
||||
uint16 flag;
|
||||
struct NODE *next;
|
||||
} NODE;
|
||||
} NODE;
|
||||
|
||||
typedef struct {
|
||||
char *buf;
|
||||
int4 state;
|
||||
int4 count;
|
||||
typedef struct
|
||||
{
|
||||
char *buf;
|
||||
int4 state;
|
||||
int4 count;
|
||||
/* reverse polish notation in list (for temprorary usage) */
|
||||
NODE *str;
|
||||
NODE *str;
|
||||
/* number in str */
|
||||
int4 num;
|
||||
int4 num;
|
||||
|
||||
/* user-friendly operand */
|
||||
int4 lenop;
|
||||
int4 sumlen;
|
||||
char *op;
|
||||
char *curop;
|
||||
} QPRS_STATE;
|
||||
int4 lenop;
|
||||
int4 sumlen;
|
||||
char *op;
|
||||
char *curop;
|
||||
} QPRS_STATE;
|
||||
|
||||
/*
|
||||
* get token from query string
|
||||
@@ -70,27 +73,31 @@ gettoken_query(QPRS_STATE * state, int4 *val, int4 *lenval, char **strval, uint1
|
||||
(state->buf)++;
|
||||
return OPEN;
|
||||
}
|
||||
else if ( ISALNUM(*(state->buf)) )
|
||||
else if (ISALNUM(*(state->buf)))
|
||||
{
|
||||
state->state = INOPERAND;
|
||||
*strval = state->buf;
|
||||
*lenval = 1;
|
||||
*flag = 0;
|
||||
} else if ( !isspace((unsigned int)*(state->buf)) )
|
||||
elog(ERROR,"Operand syntax error");
|
||||
}
|
||||
else if (!isspace((unsigned int) *(state->buf)))
|
||||
elog(ERROR, "Operand syntax error");
|
||||
break;
|
||||
case INOPERAND:
|
||||
if ( ISALNUM(*(state->buf)) ) {
|
||||
if ( *flag )
|
||||
elog(ERROR,"Modificators syntax error");
|
||||
if (ISALNUM(*(state->buf)))
|
||||
{
|
||||
if (*flag)
|
||||
elog(ERROR, "Modificators syntax error");
|
||||
(*lenval)++;
|
||||
} else if ( *(state->buf) == '%' ) {
|
||||
}
|
||||
else if (*(state->buf) == '%')
|
||||
*flag |= LVAR_SUBLEXEM;
|
||||
} else if ( *(state->buf) == '@' ) {
|
||||
else if (*(state->buf) == '@')
|
||||
*flag |= LVAR_INCASE;
|
||||
} else if ( *(state->buf) == '*' ) {
|
||||
else if (*(state->buf) == '*')
|
||||
*flag |= LVAR_ANYEND;
|
||||
} else {
|
||||
else
|
||||
{
|
||||
state->state = WAITOPERATOR;
|
||||
return VAL;
|
||||
}
|
||||
@@ -129,7 +136,7 @@ gettoken_query(QPRS_STATE * state, int4 *val, int4 *lenval, char **strval, uint1
|
||||
static void
|
||||
pushquery(QPRS_STATE * state, int4 type, int4 val, int4 distance, int4 lenval, uint16 flag)
|
||||
{
|
||||
NODE *tmp = (NODE *) palloc(sizeof(NODE));
|
||||
NODE *tmp = (NODE *) palloc(sizeof(NODE));
|
||||
|
||||
tmp->type = type;
|
||||
tmp->val = val;
|
||||
@@ -159,7 +166,7 @@ pushval_asis(QPRS_STATE * state, int type, char *strval, int lenval, uint16 flag
|
||||
|
||||
while (state->curop - state->op + lenval + 1 >= state->lenop)
|
||||
{
|
||||
int4 tmp = state->curop - state->op;
|
||||
int4 tmp = state->curop - state->op;
|
||||
|
||||
state->lenop *= 2;
|
||||
state->op = (char *) repalloc((void *) state->op, state->lenop);
|
||||
@@ -173,26 +180,27 @@ pushval_asis(QPRS_STATE * state, int type, char *strval, int lenval, uint16 flag
|
||||
return;
|
||||
}
|
||||
|
||||
#define STACKDEPTH 32
|
||||
#define STACKDEPTH 32
|
||||
/*
|
||||
* make polish notaion of query
|
||||
*/
|
||||
static int4
|
||||
makepol(QPRS_STATE * state)
|
||||
{
|
||||
int4 val,
|
||||
int4 val,
|
||||
type;
|
||||
int4 lenval;
|
||||
char *strval;
|
||||
int4 stack[STACKDEPTH];
|
||||
int4 lenstack = 0;
|
||||
uint16 flag;
|
||||
int4 lenval;
|
||||
char *strval;
|
||||
int4 stack[STACKDEPTH];
|
||||
int4 lenstack = 0;
|
||||
uint16 flag;
|
||||
|
||||
while ((type = gettoken_query(state, &val, &lenval, &strval,&flag)) != END) {
|
||||
while ((type = gettoken_query(state, &val, &lenval, &strval, &flag)) != END)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case VAL:
|
||||
pushval_asis(state, VAL, strval, lenval,flag);
|
||||
pushval_asis(state, VAL, strval, lenval, flag);
|
||||
while (lenstack && (stack[lenstack - 1] == (int4) '&' ||
|
||||
stack[lenstack - 1] == (int4) '!'))
|
||||
{
|
||||
@@ -236,7 +244,8 @@ makepol(QPRS_STATE * state)
|
||||
|
||||
}
|
||||
}
|
||||
while (lenstack) {
|
||||
while (lenstack)
|
||||
{
|
||||
lenstack--;
|
||||
pushquery(state, OPR, stack[lenstack], 0, 0, 0);
|
||||
};
|
||||
@@ -259,8 +268,8 @@ findoprnd(ITEM * ptr, int4 *pos)
|
||||
}
|
||||
else
|
||||
{
|
||||
ITEM *curitem = &ptr[*pos];
|
||||
int4 tmp = *pos;
|
||||
ITEM *curitem = &ptr[*pos];
|
||||
int4 tmp = *pos;
|
||||
|
||||
(*pos)++;
|
||||
findoprnd(ptr, pos);
|
||||
@@ -276,16 +285,16 @@ findoprnd(ITEM * ptr, int4 *pos)
|
||||
static ltxtquery *
|
||||
queryin(char *buf)
|
||||
{
|
||||
QPRS_STATE state;
|
||||
int4 i;
|
||||
QPRS_STATE state;
|
||||
int4 i;
|
||||
ltxtquery *query;
|
||||
int4 commonlen;
|
||||
ITEM *ptr;
|
||||
NODE *tmp;
|
||||
int4 pos = 0;
|
||||
int4 commonlen;
|
||||
ITEM *ptr;
|
||||
NODE *tmp;
|
||||
int4 pos = 0;
|
||||
|
||||
#ifdef BS_DEBUG
|
||||
char pbuf[16384],
|
||||
char pbuf[16384],
|
||||
*cur;
|
||||
#endif
|
||||
|
||||
@@ -351,12 +360,12 @@ ltxtq_in(PG_FUNCTION_ARGS)
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
ITEM *curpol;
|
||||
char *buf;
|
||||
char *cur;
|
||||
char *op;
|
||||
int4 buflen;
|
||||
} INFIX;
|
||||
ITEM *curpol;
|
||||
char *buf;
|
||||
char *cur;
|
||||
char *op;
|
||||
int4 buflen;
|
||||
} INFIX;
|
||||
|
||||
#define RESIZEBUF(inf,addsize) \
|
||||
while( ( inf->cur - inf->buf ) + addsize + 1 >= inf->buflen ) \
|
||||
@@ -376,23 +385,27 @@ infix(INFIX * in, bool first)
|
||||
{
|
||||
if (in->curpol->type == VAL)
|
||||
{
|
||||
char *op = in->op + in->curpol->distance;
|
||||
char *op = in->op + in->curpol->distance;
|
||||
|
||||
RESIZEBUF(in, in->curpol->length * 2 + 5);
|
||||
while (*op) {
|
||||
while (*op)
|
||||
{
|
||||
*(in->cur) = *op;
|
||||
op++;
|
||||
in->cur++;
|
||||
}
|
||||
if ( in->curpol->flag & LVAR_SUBLEXEM ) {
|
||||
if (in->curpol->flag & LVAR_SUBLEXEM)
|
||||
{
|
||||
*(in->cur) = '%';
|
||||
in->cur++;
|
||||
}
|
||||
if ( in->curpol->flag & LVAR_INCASE ) {
|
||||
if (in->curpol->flag & LVAR_INCASE)
|
||||
{
|
||||
*(in->cur) = '@';
|
||||
in->cur++;
|
||||
}
|
||||
if ( in->curpol->flag & LVAR_ANYEND ) {
|
||||
if (in->curpol->flag & LVAR_ANYEND)
|
||||
{
|
||||
*(in->cur) = '*';
|
||||
in->cur++;
|
||||
}
|
||||
@@ -401,7 +414,7 @@ infix(INFIX * in, bool first)
|
||||
}
|
||||
else if (in->curpol->val == (int4) '!')
|
||||
{
|
||||
bool isopr = false;
|
||||
bool isopr = false;
|
||||
|
||||
RESIZEBUF(in, 1);
|
||||
*(in->cur) = '!';
|
||||
@@ -425,8 +438,8 @@ infix(INFIX * in, bool first)
|
||||
}
|
||||
else
|
||||
{
|
||||
int4 op = in->curpol->val;
|
||||
INFIX nrm;
|
||||
int4 op = in->curpol->val;
|
||||
INFIX nrm;
|
||||
|
||||
in->curpol++;
|
||||
if (op == (int4) '|' && !first)
|
||||
@@ -467,7 +480,7 @@ Datum
|
||||
ltxtq_out(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ltxtquery *query = PG_GETARG_LTXTQUERY(0);
|
||||
INFIX nrm;
|
||||
INFIX nrm;
|
||||
|
||||
if (query->size == 0)
|
||||
elog(ERROR, "Empty");
|
||||
@@ -481,4 +494,3 @@ ltxtq_out(PG_FUNCTION_ARGS)
|
||||
PG_FREE_IF_COPY(query, 0);
|
||||
PG_RETURN_POINTER(nrm.buf);
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* txtquery operations with ltree
|
||||
* txtquery operations with ltree
|
||||
* Teodor Sigaev <teodor@stack.net>
|
||||
*/
|
||||
|
||||
@@ -12,20 +12,26 @@ PG_FUNCTION_INFO_V1(ltxtq_rexec);
|
||||
/*
|
||||
* check for boolean condition
|
||||
*/
|
||||
bool
|
||||
ltree_execute(ITEM * curitem, void *checkval, bool calcnot, bool (*chkcond) (void *checkval, ITEM * val)) {
|
||||
bool
|
||||
ltree_execute(ITEM * curitem, void *checkval, bool calcnot, bool (*chkcond) (void *checkval, ITEM * val))
|
||||
{
|
||||
if (curitem->type == VAL)
|
||||
return (*chkcond) (checkval, curitem);
|
||||
else if (curitem->val == (int4) '!') {
|
||||
else if (curitem->val == (int4) '!')
|
||||
{
|
||||
return (calcnot) ?
|
||||
((ltree_execute(curitem + 1, checkval, calcnot, chkcond)) ? false : true)
|
||||
: true;
|
||||
} else if (curitem->val == (int4) '&') {
|
||||
if (ltree_execute(curitem + curitem->left, checkval, calcnot, chkcond))
|
||||
}
|
||||
else if (curitem->val == (int4) '&')
|
||||
{
|
||||
if (ltree_execute(curitem + curitem->left, checkval, calcnot, chkcond))
|
||||
return ltree_execute(curitem + 1, checkval, calcnot, chkcond);
|
||||
else
|
||||
return false;
|
||||
} else { /* |-operator */
|
||||
}
|
||||
else
|
||||
{ /* |-operator */
|
||||
if (ltree_execute(curitem + curitem->left, checkval, calcnot, chkcond))
|
||||
return true;
|
||||
else
|
||||
@@ -34,54 +40,60 @@ ltree_execute(ITEM * curitem, void *checkval, bool calcnot, bool (*chkcond) (voi
|
||||
return false;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
ltree *node;
|
||||
char *operand;
|
||||
} CHKVAL;
|
||||
typedef struct
|
||||
{
|
||||
ltree *node;
|
||||
char *operand;
|
||||
} CHKVAL;
|
||||
|
||||
static bool
|
||||
checkcondition_str(void* checkval, ITEM * val) {
|
||||
ltree_level *level = LTREE_FIRST( ((CHKVAL*)checkval)->node );
|
||||
int tlen = ((CHKVAL*)checkval)->node->numlevel;
|
||||
char *op = ((CHKVAL*)checkval)->operand + val->distance;
|
||||
int (*cmpptr)(const char *,const char *,size_t);
|
||||
checkcondition_str(void *checkval, ITEM * val)
|
||||
{
|
||||
ltree_level *level = LTREE_FIRST(((CHKVAL *) checkval)->node);
|
||||
int tlen = ((CHKVAL *) checkval)->node->numlevel;
|
||||
char *op = ((CHKVAL *) checkval)->operand + val->distance;
|
||||
int (*cmpptr) (const char *, const char *, size_t);
|
||||
|
||||
cmpptr = ( val->flag & LVAR_INCASE ) ? strncasecmp : strncmp;
|
||||
while( tlen > 0 ) {
|
||||
if ( val->flag & LVAR_SUBLEXEM ) {
|
||||
if ( compare_subnode(level, op, val->length, cmpptr, (val->flag & LVAR_ANYEND) ) )
|
||||
cmpptr = (val->flag & LVAR_INCASE) ? strncasecmp : strncmp;
|
||||
while (tlen > 0)
|
||||
{
|
||||
if (val->flag & LVAR_SUBLEXEM)
|
||||
{
|
||||
if (compare_subnode(level, op, val->length, cmpptr, (val->flag & LVAR_ANYEND)))
|
||||
return true;
|
||||
} else if (
|
||||
(
|
||||
val->length == level->len ||
|
||||
( level->len > val->length && (val->flag & LVAR_ANYEND) )
|
||||
) &&
|
||||
(*cmpptr)( op, level->name, val->length) == 0 )
|
||||
}
|
||||
else if (
|
||||
(
|
||||
val->length == level->len ||
|
||||
(level->len > val->length && (val->flag & LVAR_ANYEND))
|
||||
) &&
|
||||
(*cmpptr) (op, level->name, val->length) == 0)
|
||||
return true;
|
||||
|
||||
tlen--;
|
||||
level = LEVEL_NEXT(level);
|
||||
level = LEVEL_NEXT(level);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Datum
|
||||
ltxtq_exec(PG_FUNCTION_ARGS) {
|
||||
ltree *val = PG_GETARG_LTREE(0);
|
||||
ltxtq_exec(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ltree *val = PG_GETARG_LTREE(0);
|
||||
ltxtquery *query = PG_GETARG_LTXTQUERY(1);
|
||||
CHKVAL chkval;
|
||||
bool result;
|
||||
CHKVAL chkval;
|
||||
bool result;
|
||||
|
||||
chkval.node = val;
|
||||
chkval.operand = GETOPERAND(query);
|
||||
|
||||
result = ltree_execute(
|
||||
GETQUERY(query),
|
||||
&chkval,
|
||||
true,
|
||||
checkcondition_str
|
||||
);
|
||||
GETQUERY(query),
|
||||
&chkval,
|
||||
true,
|
||||
checkcondition_str
|
||||
);
|
||||
|
||||
PG_FREE_IF_COPY(val, 0);
|
||||
PG_FREE_IF_COPY(query, 1);
|
||||
@@ -89,11 +101,10 @@ ltxtq_exec(PG_FUNCTION_ARGS) {
|
||||
}
|
||||
|
||||
Datum
|
||||
ltxtq_rexec(PG_FUNCTION_ARGS) {
|
||||
PG_RETURN_DATUM( DirectFunctionCall2( ltxtq_exec,
|
||||
PG_GETARG_DATUM(1),
|
||||
PG_GETARG_DATUM(0)
|
||||
) );
|
||||
ltxtq_rexec(PG_FUNCTION_ARGS)
|
||||
{
|
||||
PG_RETURN_DATUM(DirectFunctionCall2(ltxtq_exec,
|
||||
PG_GETARG_DATUM(1),
|
||||
PG_GETARG_DATUM(0)
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
|
@@ -263,7 +263,7 @@ msqlListTables(int a)
|
||||
char tbuf[BUFSIZ];
|
||||
|
||||
snprintf(tbuf, BUFSIZ,
|
||||
"select relname from pg_class where relkind='r' and relowner=%d",
|
||||
"select relname from pg_class where relkind='r' and relowner=%d",
|
||||
getuid());
|
||||
if (msqlQuery(a, tbuf) > 0)
|
||||
{
|
||||
@@ -287,7 +287,7 @@ msqlListIndex(int a, char *b, char *c)
|
||||
char tbuf[BUFSIZ];
|
||||
|
||||
snprintf(tbuf, BUFSIZ,
|
||||
"select relname from pg_class where relkind='i' and relowner=%d",
|
||||
"select relname from pg_class where relkind='i' and relowner=%d",
|
||||
getuid());
|
||||
if (msqlQuery(a, tbuf) > 0)
|
||||
{
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/* -------------------------------------------------------------------------
|
||||
* pg_dumplo
|
||||
*
|
||||
* $Header: /cvsroot/pgsql/contrib/pg_dumplo/Attic/lo_export.c,v 1.9 2002/08/15 02:58:29 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/contrib/pg_dumplo/Attic/lo_export.c,v 1.10 2002/09/04 20:31:06 momjian Exp $
|
||||
*
|
||||
* Karel Zak 1999-2000
|
||||
* -------------------------------------------------------------------------
|
||||
@@ -142,7 +142,7 @@ pglo_export(LODumpMaster * pgLO)
|
||||
{
|
||||
|
||||
snprintf(path, BUFSIZ, "%s/%s/%s", pgLO->space, pgLO->db,
|
||||
ll->lo_table);
|
||||
ll->lo_table);
|
||||
|
||||
if (mkdir(path, DIR_UMASK) == -1)
|
||||
{
|
||||
@@ -154,7 +154,7 @@ pglo_export(LODumpMaster * pgLO)
|
||||
}
|
||||
|
||||
snprintf(path, BUFSIZ, "%s/%s/%s/%s", pgLO->space, pgLO->db,
|
||||
ll->lo_table, ll->lo_attr);
|
||||
ll->lo_table, ll->lo_attr);
|
||||
|
||||
if (mkdir(path, DIR_UMASK) == -1)
|
||||
{
|
||||
@@ -187,7 +187,7 @@ pglo_export(LODumpMaster * pgLO)
|
||||
}
|
||||
|
||||
snprintf(path, BUFSIZ, "%s/%s/%s/%s/%s", pgLO->space,
|
||||
pgLO->db, ll->lo_table, ll->lo_attr, val);
|
||||
pgLO->db, ll->lo_table, ll->lo_attr, val);
|
||||
|
||||
if (lo_export(pgLO->conn, lo, path) < 0)
|
||||
fprintf(stderr, "%s: lo_export failed:\n%s", progname,
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/* -------------------------------------------------------------------------
|
||||
* pg_dumplo
|
||||
*
|
||||
* $Header: /cvsroot/pgsql/contrib/pg_dumplo/Attic/lo_import.c,v 1.7 2002/08/15 02:58:29 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/contrib/pg_dumplo/Attic/lo_import.c,v 1.8 2002/09/04 20:31:06 momjian Exp $
|
||||
*
|
||||
* Karel Zak 1999-2000
|
||||
* -------------------------------------------------------------------------
|
||||
@@ -82,7 +82,7 @@ pglo_import(LODumpMaster * pgLO)
|
||||
* UPDATE oid in tab
|
||||
*/
|
||||
snprintf(Qbuff, QUERY_BUFSIZ,
|
||||
"UPDATE \"%s\" SET \"%s\"=%u WHERE \"%s\"=%u",
|
||||
"UPDATE \"%s\" SET \"%s\"=%u WHERE \"%s\"=%u",
|
||||
loa.lo_table, loa.lo_attr, new_oid, loa.lo_attr, loa.lo_oid);
|
||||
|
||||
/* fprintf(stderr, Qbuff); */
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/* -------------------------------------------------------------------------
|
||||
* pg_dumplo
|
||||
*
|
||||
* $Header: /cvsroot/pgsql/contrib/pg_dumplo/Attic/main.c,v 1.10 2001/11/12 17:44:14 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/contrib/pg_dumplo/Attic/main.c,v 1.11 2002/09/04 20:31:07 momjian Exp $
|
||||
*
|
||||
* Karel Zak 1999-2000
|
||||
* -------------------------------------------------------------------------
|
||||
@@ -284,7 +284,7 @@ usage()
|
||||
"-p --password=<password> password for connection to server\n"
|
||||
"-d --db=<database> database name\n"
|
||||
"-t --host=<hostname> server hostname\n"
|
||||
"-o --port=<port> database server port (default: 5432)\n"
|
||||
"-o --port=<port> database server port (default: 5432)\n"
|
||||
"-s --space=<dir> directory with dump tree (for export/import)\n"
|
||||
"-i --import import large obj dump tree to DB\n"
|
||||
"-e --export export (dump) large obj to dump tree\n"
|
||||
@@ -301,7 +301,7 @@ usage()
|
||||
"-p <password> password for connection to server\n"
|
||||
"-d <database> database name\n"
|
||||
"-t <hostname> server hostname\n"
|
||||
"-o <port> database server port (default: 5432)\n"
|
||||
"-o <port> database server port (default: 5432)\n"
|
||||
"-s <dir> directory with dump tree (for export/import)\n"
|
||||
"-i import large obj dump tree to DB\n"
|
||||
"-e export (dump) large obj to dump tree\n"
|
||||
|
@@ -1,10 +1,10 @@
|
||||
/*
|
||||
* $Header: /cvsroot/pgsql/contrib/pgbench/pgbench.c,v 1.18 2002/08/15 02:58:29 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/contrib/pgbench/pgbench.c,v 1.19 2002/09/04 20:31:08 momjian Exp $
|
||||
*
|
||||
* pgbench: a simple TPC-B like benchmark program for PostgreSQL
|
||||
* written by Tatsuo Ishii
|
||||
*
|
||||
* Copyright (c) 2000-2002 Tatsuo Ishii
|
||||
* Copyright (c) 2000-2002 Tatsuo Ishii
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software and
|
||||
* its documentation for any purpose and without fee is hereby
|
||||
@@ -88,8 +88,8 @@ typedef struct
|
||||
int state; /* state No. */
|
||||
int cnt; /* xacts count */
|
||||
int ecnt; /* error count */
|
||||
int listen; /* 0 indicates that an async query
|
||||
* has been sent */
|
||||
int listen; /* 0 indicates that an async query has
|
||||
* been sent */
|
||||
int aid; /* account id for this transaction */
|
||||
int bid; /* branch id for this transaction */
|
||||
int tid; /* teller id for this transaction */
|
||||
@@ -241,15 +241,19 @@ doOne(CState * state, int n, int debug, int ttype)
|
||||
discard_response(st);
|
||||
break;
|
||||
case 6: /* response to "end" */
|
||||
/* transaction finished: record the time it took in the log */
|
||||
|
||||
/*
|
||||
* transaction finished: record the time it took in the
|
||||
* log
|
||||
*/
|
||||
if (use_log)
|
||||
{
|
||||
long long diff;
|
||||
long long diff;
|
||||
struct timeval now;
|
||||
|
||||
gettimeofday(&now, 0);
|
||||
diff = (now.tv_sec - st->txn_begin.tv_sec) * 1000000 +
|
||||
(now.tv_usec - st->txn_begin.tv_usec);
|
||||
(now.tv_usec - st->txn_begin.tv_usec);
|
||||
|
||||
fprintf(LOGFILE, "%d %d %lld\n", st->id, st->cnt, diff);
|
||||
}
|
||||
@@ -318,19 +322,19 @@ doOne(CState * state, int n, int debug, int ttype)
|
||||
case 3:
|
||||
if (ttype == 0)
|
||||
{
|
||||
snprintf(sql, 256, "update tellers set tbalance = tbalance + %d where tid = %d\n",
|
||||
st->delta, st->tid);
|
||||
break;
|
||||
snprintf(sql, 256, "update tellers set tbalance = tbalance + %d where tid = %d\n",
|
||||
st->delta, st->tid);
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
if (ttype == 0)
|
||||
{
|
||||
snprintf(sql, 256, "update branches set bbalance = bbalance + %d where bid = %d", st->delta, st->bid);
|
||||
break;
|
||||
snprintf(sql, 256, "update branches set bbalance = bbalance + %d where bid = %d", st->delta, st->bid);
|
||||
break;
|
||||
}
|
||||
case 5:
|
||||
snprintf(sql, 256, "insert into history(tid,bid,aid,delta,mtime) values(%d,%d,%d,%d,'now')",
|
||||
st->tid, st->bid, st->aid, st->delta);
|
||||
st->tid, st->bid, st->aid, st->delta);
|
||||
break;
|
||||
case 6:
|
||||
strcpy(sql, "end");
|
||||
@@ -513,7 +517,7 @@ init(void)
|
||||
for (i = 0; i < ntellers * tps; i++)
|
||||
{
|
||||
snprintf(sql, 256, "insert into tellers(tid,bid,tbalance) values (%d,%d,0)"
|
||||
,i + 1, i / ntellers + 1);
|
||||
,i + 1, i / ntellers + 1);
|
||||
res = PQexec(con, sql);
|
||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
||||
{
|
||||
@@ -577,6 +581,7 @@ init(void)
|
||||
}
|
||||
|
||||
#ifdef NOT_USED
|
||||
|
||||
/*
|
||||
* do a checkpoint to purge the old WAL logs
|
||||
*/
|
||||
@@ -586,7 +591,7 @@ init(void)
|
||||
fprintf(stderr, "%s", PQerrorMessage(con));
|
||||
exit(1);
|
||||
}
|
||||
#endif /* NOT_USED */
|
||||
#endif /* NOT_USED */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -614,7 +619,7 @@ printResults(
|
||||
t2;
|
||||
int i;
|
||||
int normal_xacts = 0;
|
||||
char *s;
|
||||
char *s;
|
||||
|
||||
for (i = 0; i < nclients; i++)
|
||||
normal_xacts += state[i].cnt;
|
||||
@@ -626,11 +631,11 @@ printResults(
|
||||
t2 = normal_xacts * 1000000.0 / t2;
|
||||
|
||||
if (ttype == 0)
|
||||
s = "TPC-B (sort of)";
|
||||
s = "TPC-B (sort of)";
|
||||
else if (ttype == 2)
|
||||
s = "Update only accounts";
|
||||
s = "Update only accounts";
|
||||
else
|
||||
s = "SELECT only";
|
||||
s = "SELECT only";
|
||||
|
||||
printf("transaction type: %s\n", s);
|
||||
printf("scaling factor: %d\n", tps);
|
||||
@@ -655,8 +660,9 @@ main(int argc, char **argv)
|
||||
* testing? */
|
||||
int is_full_vacuum = 0; /* do full vacuum before testing? */
|
||||
int debug = 0; /* debug flag */
|
||||
int ttype = 0; /* transaction type. 0: TPC-B, 1: SELECT only,
|
||||
* 2: skip update of branches and tellers */
|
||||
int ttype = 0; /* transaction type. 0: TPC-B, 1: SELECT
|
||||
* only, 2: skip update of branches and
|
||||
* tellers */
|
||||
|
||||
static CState *state; /* status of clients */
|
||||
|
||||
@@ -789,7 +795,7 @@ main(int argc, char **argv)
|
||||
|
||||
if (use_log)
|
||||
{
|
||||
char logpath[64];
|
||||
char logpath[64];
|
||||
|
||||
snprintf(logpath, 64, "pgbench_log.%d", getpid());
|
||||
LOGFILE = fopen(logpath, "w");
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* $Header: /cvsroot/pgsql/contrib/pgstattuple/pgstattuple.c,v 1.8 2002/08/29 17:14:31 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/contrib/pgstattuple/pgstattuple.c,v 1.9 2002/09/04 20:31:08 momjian Exp $
|
||||
*
|
||||
* Copyright (c) 2001,2002 Tatsuo Ishii
|
||||
* Copyright (c) 2001,2002 Tatsuo Ishii
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software and
|
||||
* its documentation for any purpose, without fee, and without a
|
||||
@@ -71,12 +71,12 @@ pgstattuple(PG_FUNCTION_ARGS)
|
||||
double dead_tuple_percent;
|
||||
uint64 free_space = 0; /* free/reusable space in bytes */
|
||||
double free_percent; /* free/reusable space in % */
|
||||
TupleDesc tupdesc;
|
||||
TupleTableSlot *slot;
|
||||
AttInMetadata *attinmeta;
|
||||
char **values;
|
||||
int i;
|
||||
Datum result;
|
||||
TupleDesc tupdesc;
|
||||
TupleTableSlot *slot;
|
||||
AttInMetadata *attinmeta;
|
||||
char **values;
|
||||
int i;
|
||||
Datum result;
|
||||
|
||||
/*
|
||||
* Build a tuple description for a pgstattupe_type tuple
|
||||
@@ -94,7 +94,7 @@ pgstattuple(PG_FUNCTION_ARGS)
|
||||
|
||||
/* open relation */
|
||||
relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname,
|
||||
"pgstattuple"));
|
||||
"pgstattuple"));
|
||||
rel = heap_openrv(relrv, AccessShareLock);
|
||||
|
||||
nblocks = RelationGetNumberOfBlocks(rel);
|
||||
@@ -116,7 +116,7 @@ pgstattuple(PG_FUNCTION_ARGS)
|
||||
|
||||
/*
|
||||
* To avoid physically reading the table twice, try to do the
|
||||
* free-space scan in parallel with the heap scan. However,
|
||||
* free-space scan in parallel with the heap scan. However,
|
||||
* heap_getnext may find no tuples on a given page, so we cannot
|
||||
* simply examine the pages returned by the heap scan.
|
||||
*/
|
||||
@@ -142,7 +142,7 @@ pgstattuple(PG_FUNCTION_ARGS)
|
||||
|
||||
heap_close(rel, AccessShareLock);
|
||||
|
||||
table_len = (uint64)nblocks *BLCKSZ;
|
||||
table_len = (uint64) nblocks *BLCKSZ;
|
||||
|
||||
if (nblocks == 0)
|
||||
{
|
||||
@@ -158,15 +158,13 @@ pgstattuple(PG_FUNCTION_ARGS)
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare a values array for storage in our slot.
|
||||
* This should be an array of C strings which will
|
||||
* be processed later by the appropriate "in" functions.
|
||||
* Prepare a values array for storage in our slot. This should be an
|
||||
* array of C strings which will be processed later by the appropriate
|
||||
* "in" functions.
|
||||
*/
|
||||
values = (char **) palloc(NCOLUMNS * sizeof(char *));
|
||||
for (i=0;i<NCOLUMNS;i++)
|
||||
{
|
||||
for (i = 0; i < NCOLUMNS; i++)
|
||||
values[i] = (char *) palloc(NCHARS * sizeof(char));
|
||||
}
|
||||
i = 0;
|
||||
snprintf(values[i++], NCHARS, "%lld", table_len);
|
||||
snprintf(values[i++], NCHARS, "%lld", tuple_count);
|
||||
@@ -177,19 +175,17 @@ pgstattuple(PG_FUNCTION_ARGS)
|
||||
snprintf(values[i++], NCHARS, "%.2f", dead_tuple_percent);
|
||||
snprintf(values[i++], NCHARS, "%lld", free_space);
|
||||
snprintf(values[i++], NCHARS, "%.2f", free_percent);
|
||||
|
||||
|
||||
/* build a tuple */
|
||||
tuple = BuildTupleFromCStrings(attinmeta, values);
|
||||
|
||||
|
||||
/* make the tuple into a datum */
|
||||
result = TupleGetDatum(slot, tuple);
|
||||
|
||||
|
||||
/* Clean up */
|
||||
for (i=0;i<NCOLUMNS;i++)
|
||||
{
|
||||
for (i = 0; i < NCOLUMNS; i++)
|
||||
pfree(values[i]);
|
||||
}
|
||||
pfree(values);
|
||||
|
||||
|
||||
PG_RETURN_DATUM(result);
|
||||
}
|
||||
|
@@ -128,8 +128,8 @@ _rserv_log_()
|
||||
okey = key;
|
||||
|
||||
snprintf(sql, 8192, "update _RSERV_LOG_ set logid = %d, logtime = now(), "
|
||||
"deleted = %d where reloid = %u and key = '%s'",
|
||||
GetCurrentTransactionId(), deleted, rel->rd_id, okey);
|
||||
"deleted = %d where reloid = %u and key = '%s'",
|
||||
GetCurrentTransactionId(), deleted, rel->rd_id, okey);
|
||||
|
||||
if (debug)
|
||||
elog(DEBUG3, sql);
|
||||
@@ -147,10 +147,10 @@ _rserv_log_()
|
||||
else if (SPI_processed == 0)
|
||||
{
|
||||
snprintf(sql, 8192, "insert into _RSERV_LOG_ "
|
||||
"(reloid, logid, logtime, deleted, key) "
|
||||
"values (%u, %d, now(), %d, '%s')",
|
||||
rel->rd_id, GetCurrentTransactionId(),
|
||||
deleted, okey);
|
||||
"(reloid, logid, logtime, deleted, key) "
|
||||
"values (%u, %d, now(), %d, '%s')",
|
||||
rel->rd_id, GetCurrentTransactionId(),
|
||||
deleted, okey);
|
||||
|
||||
if (debug)
|
||||
elog(DEBUG3, sql);
|
||||
@@ -172,9 +172,9 @@ _rserv_log_()
|
||||
okey = newkey;
|
||||
|
||||
snprintf(sql, 8192, "insert into _RSERV_LOG_ "
|
||||
"(reloid, logid, logtime, deleted, key) "
|
||||
"values (%u, %d, now(), 0, '%s')",
|
||||
rel->rd_id, GetCurrentTransactionId(), okey);
|
||||
"(reloid, logid, logtime, deleted, key) "
|
||||
"values (%u, %d, now(), 0, '%s')",
|
||||
rel->rd_id, GetCurrentTransactionId(), okey);
|
||||
|
||||
if (debug)
|
||||
elog(DEBUG3, sql);
|
||||
@@ -221,17 +221,17 @@ _rserv_sync_(int32 server)
|
||||
for (xcnt = 0; xcnt < SerializableSnapshot->xcnt; xcnt++)
|
||||
{
|
||||
snprintf(buf + strlen(buf), 8192 - strlen(buf),
|
||||
"%s%u", (xcnt) ? ", " : "",
|
||||
SerializableSnapshot->xip[xcnt]);
|
||||
"%s%u", (xcnt) ? ", " : "",
|
||||
SerializableSnapshot->xip[xcnt]);
|
||||
}
|
||||
|
||||
if ((ret = SPI_connect()) < 0)
|
||||
elog(ERROR, "_rserv_sync_: SPI_connect returned %d", ret);
|
||||
|
||||
snprintf(sql, 8192, "insert into _RSERV_SYNC_ "
|
||||
"(server, syncid, synctime, status, minid, maxid, active) "
|
||||
"(server, syncid, synctime, status, minid, maxid, active) "
|
||||
"values (%u, currval('_rserv_sync_seq_'), now(), 0, %d, %d, '%s')",
|
||||
server, SerializableSnapshot->xmin, SerializableSnapshot->xmax, active);
|
||||
server, SerializableSnapshot->xmin, SerializableSnapshot->xmax, active);
|
||||
|
||||
ret = SPI_exec(sql, 0);
|
||||
|
||||
|
@@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/contrib/rtree_gist/Attic/rtree_gist.c,v 1.5 2002/05/28 15:24:53 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/contrib/rtree_gist/Attic/rtree_gist.c,v 1.6 2002/09/04 20:31:08 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -161,20 +161,23 @@ gbox_penalty(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
BOX *key;
|
||||
int pos;
|
||||
} KBsort;
|
||||
typedef struct
|
||||
{
|
||||
BOX *key;
|
||||
int pos;
|
||||
} KBsort;
|
||||
|
||||
static int
|
||||
compare_KB(const void* a, const void* b) {
|
||||
BOX *abox = ((KBsort*)a)->key;
|
||||
BOX *bbox = ((KBsort*)b)->key;
|
||||
float sa = (abox->high.x - abox->low.x) * (abox->high.y - abox->low.y);
|
||||
float sb = (bbox->high.x - bbox->low.x) * (bbox->high.y - bbox->low.y);
|
||||
compare_KB(const void *a, const void *b)
|
||||
{
|
||||
BOX *abox = ((KBsort *) a)->key;
|
||||
BOX *bbox = ((KBsort *) b)->key;
|
||||
float sa = (abox->high.x - abox->low.x) * (abox->high.y - abox->low.y);
|
||||
float sb = (bbox->high.x - bbox->low.x) * (bbox->high.y - bbox->low.y);
|
||||
|
||||
if ( sa==sb ) return 0;
|
||||
return ( sa>sb ) ? 1 : -1;
|
||||
if (sa == sb)
|
||||
return 0;
|
||||
return (sa > sb) ? 1 : -1;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -217,14 +220,14 @@ gbox_picksplit(PG_FUNCTION_ARGS)
|
||||
for (i = OffsetNumberNext(FirstOffsetNumber); i <= maxoff; i = OffsetNumberNext(i))
|
||||
{
|
||||
cur = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[i].key);
|
||||
if ( allisequal == true && (
|
||||
pageunion.high.x != cur->high.x ||
|
||||
pageunion.high.y != cur->high.y ||
|
||||
pageunion.low.x != cur->low.x ||
|
||||
pageunion.low.y != cur->low.y
|
||||
) )
|
||||
if (allisequal == true && (
|
||||
pageunion.high.x != cur->high.x ||
|
||||
pageunion.high.y != cur->high.y ||
|
||||
pageunion.low.x != cur->low.x ||
|
||||
pageunion.low.y != cur->low.y
|
||||
))
|
||||
allisequal = false;
|
||||
|
||||
|
||||
if (pageunion.high.x < cur->high.x)
|
||||
pageunion.high.x = cur->high.x;
|
||||
if (pageunion.low.x > cur->low.x)
|
||||
@@ -293,45 +296,53 @@ gbox_picksplit(PG_FUNCTION_ARGS)
|
||||
{
|
||||
cur = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[i].key);
|
||||
if (cur->low.x - pageunion.low.x < pageunion.high.x - cur->high.x)
|
||||
ADDLIST(listL, unionL, posL,i);
|
||||
ADDLIST(listL, unionL, posL, i);
|
||||
else
|
||||
ADDLIST(listR, unionR, posR,i);
|
||||
ADDLIST(listR, unionR, posR, i);
|
||||
if (cur->low.y - pageunion.low.y < pageunion.high.y - cur->high.y)
|
||||
ADDLIST(listB, unionB, posB,i);
|
||||
ADDLIST(listB, unionB, posB, i);
|
||||
else
|
||||
ADDLIST(listT, unionT, posT,i);
|
||||
ADDLIST(listT, unionT, posT, i);
|
||||
}
|
||||
|
||||
/* bad disposition, sort by ascending and resplit */
|
||||
if ( (posR==0 || posL==0) && (posT==0 || posB==0) ) {
|
||||
KBsort *arr = (KBsort*)palloc( sizeof(KBsort) * maxoff );
|
||||
if ((posR == 0 || posL == 0) && (posT == 0 || posB == 0))
|
||||
{
|
||||
KBsort *arr = (KBsort *) palloc(sizeof(KBsort) * maxoff);
|
||||
|
||||
posL = posR = posB = posT = 0;
|
||||
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
|
||||
arr[i-1].key = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[i].key);
|
||||
arr[i-1].pos = i;
|
||||
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
|
||||
{
|
||||
arr[i - 1].key = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[i].key);
|
||||
arr[i - 1].pos = i;
|
||||
}
|
||||
qsort( arr, maxoff, sizeof(KBsort), compare_KB );
|
||||
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
|
||||
cur = arr[i-1].key;
|
||||
qsort(arr, maxoff, sizeof(KBsort), compare_KB);
|
||||
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
|
||||
{
|
||||
cur = arr[i - 1].key;
|
||||
if (cur->low.x - pageunion.low.x < pageunion.high.x - cur->high.x)
|
||||
ADDLIST(listL, unionL, posL,arr[i-1].pos);
|
||||
else if ( cur->low.x - pageunion.low.x == pageunion.high.x - cur->high.x ) {
|
||||
if ( posL>posR )
|
||||
ADDLIST(listR, unionR, posR,arr[i-1].pos);
|
||||
ADDLIST(listL, unionL, posL, arr[i - 1].pos);
|
||||
else if (cur->low.x - pageunion.low.x == pageunion.high.x - cur->high.x)
|
||||
{
|
||||
if (posL > posR)
|
||||
ADDLIST(listR, unionR, posR, arr[i - 1].pos);
|
||||
else
|
||||
ADDLIST(listL, unionL, posL,arr[i-1].pos);
|
||||
} else
|
||||
ADDLIST(listR, unionR, posR,arr[i-1].pos);
|
||||
ADDLIST(listL, unionL, posL, arr[i - 1].pos);
|
||||
}
|
||||
else
|
||||
ADDLIST(listR, unionR, posR, arr[i - 1].pos);
|
||||
|
||||
if (cur->low.y - pageunion.low.y < pageunion.high.y - cur->high.y)
|
||||
ADDLIST(listB, unionB, posB,arr[i-1].pos);
|
||||
else if ( cur->low.y - pageunion.low.y == pageunion.high.y - cur->high.y ) {
|
||||
if ( posB>posT )
|
||||
ADDLIST(listT, unionT, posT,arr[i-1].pos);
|
||||
ADDLIST(listB, unionB, posB, arr[i - 1].pos);
|
||||
else if (cur->low.y - pageunion.low.y == pageunion.high.y - cur->high.y)
|
||||
{
|
||||
if (posB > posT)
|
||||
ADDLIST(listT, unionT, posT, arr[i - 1].pos);
|
||||
else
|
||||
ADDLIST(listB, unionB, posB,arr[i-1].pos);
|
||||
} else
|
||||
ADDLIST(listT, unionT, posT,arr[i-1].pos);
|
||||
ADDLIST(listB, unionB, posB, arr[i - 1].pos);
|
||||
}
|
||||
else
|
||||
ADDLIST(listT, unionT, posT, arr[i - 1].pos);
|
||||
}
|
||||
pfree(arr);
|
||||
}
|
||||
|
@@ -462,9 +462,9 @@ check_foreign_key(PG_FUNCTION_ARGS)
|
||||
* value
|
||||
*/
|
||||
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql),
|
||||
" %s = %s%s%s %s ",
|
||||
args2[k], (is_char_type > 0) ? "'" : "",
|
||||
nv, (is_char_type > 0) ? "'" : "", (k < nkeys) ? ", " : "");
|
||||
" %s = %s%s%s %s ",
|
||||
args2[k], (is_char_type > 0) ? "'" : "",
|
||||
nv, (is_char_type > 0) ? "'" : "", (k < nkeys) ? ", " : "");
|
||||
is_char_type = 0;
|
||||
}
|
||||
strcat(sql, " where ");
|
||||
@@ -488,8 +488,8 @@ check_foreign_key(PG_FUNCTION_ARGS)
|
||||
for (i = 1; i <= nkeys; i++)
|
||||
{
|
||||
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql),
|
||||
"%s = null%s",
|
||||
args2[i], (i < nkeys) ? ", " : "");
|
||||
"%s = null%s",
|
||||
args2[i], (i < nkeys) ? ", " : "");
|
||||
}
|
||||
strcat(sql, " where ");
|
||||
}
|
||||
@@ -498,7 +498,7 @@ check_foreign_key(PG_FUNCTION_ARGS)
|
||||
for (i = 1; i <= nkeys; i++)
|
||||
{
|
||||
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s = $%d %s",
|
||||
args2[i], i, (i < nkeys) ? "and " : "");
|
||||
args2[i], i, (i < nkeys) ? "and " : "");
|
||||
}
|
||||
|
||||
/* Prepare plan for query */
|
||||
|
@@ -270,7 +270,7 @@ timetravel(PG_FUNCTION_ARGS)
|
||||
for (i = 1; i <= natts; i++)
|
||||
{
|
||||
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "$%d%s",
|
||||
i, (i < natts) ? ", " : ")");
|
||||
i, (i < natts) ? ", " : ")");
|
||||
ctypes[i - 1] = SPI_gettypeid(tupdesc, i);
|
||||
}
|
||||
|
||||
|
@@ -11,13 +11,13 @@
|
||||
* documentation for any purpose, without fee, and without a written agreement
|
||||
* is hereby granted, provided that the above copyright notice and this
|
||||
* paragraph and the following two paragraphs appear in all copies.
|
||||
*
|
||||
*
|
||||
* IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
|
||||
* LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
|
||||
* DOCUMENTATION, EVEN IF THE AUTHOR OR DISTRIBUTORS HAVE BEEN ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*
|
||||
* THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
@@ -45,42 +45,42 @@ static bool compatCrosstabTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2);
|
||||
static bool compatConnectbyTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2);
|
||||
static void get_normal_pair(float8 *x1, float8 *x2);
|
||||
static TupleDesc make_crosstab_tupledesc(TupleDesc spi_tupdesc,
|
||||
int num_catagories);
|
||||
int num_catagories);
|
||||
static Tuplestorestate *connectby(char *relname,
|
||||
char *key_fld,
|
||||
char *parent_key_fld,
|
||||
char *branch_delim,
|
||||
char *start_with,
|
||||
int max_depth,
|
||||
bool show_branch,
|
||||
MemoryContext per_query_ctx,
|
||||
AttInMetadata *attinmeta);
|
||||
char *key_fld,
|
||||
char *parent_key_fld,
|
||||
char *branch_delim,
|
||||
char *start_with,
|
||||
int max_depth,
|
||||
bool show_branch,
|
||||
MemoryContext per_query_ctx,
|
||||
AttInMetadata *attinmeta);
|
||||
static Tuplestorestate *build_tuplestore_recursively(char *key_fld,
|
||||
char *parent_key_fld,
|
||||
char *relname,
|
||||
char *branch_delim,
|
||||
char *start_with,
|
||||
char *branch,
|
||||
int level,
|
||||
int max_depth,
|
||||
bool show_branch,
|
||||
MemoryContext per_query_ctx,
|
||||
AttInMetadata *attinmeta,
|
||||
Tuplestorestate *tupstore);
|
||||
char *parent_key_fld,
|
||||
char *relname,
|
||||
char *branch_delim,
|
||||
char *start_with,
|
||||
char *branch,
|
||||
int level,
|
||||
int max_depth,
|
||||
bool show_branch,
|
||||
MemoryContext per_query_ctx,
|
||||
AttInMetadata *attinmeta,
|
||||
Tuplestorestate *tupstore);
|
||||
static char *quote_ident_cstr(char *rawstr);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float8 mean; /* mean of the distribution */
|
||||
float8 stddev; /* stddev of the distribution */
|
||||
float8 carry_val; /* hold second generated value */
|
||||
bool use_carry; /* use second generated value */
|
||||
float8 mean; /* mean of the distribution */
|
||||
float8 stddev; /* stddev of the distribution */
|
||||
float8 carry_val; /* hold second generated value */
|
||||
bool use_carry; /* use second generated value */
|
||||
} normal_rand_fctx;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SPITupleTable *spi_tuptable; /* sql results from user query */
|
||||
char *lastrowid; /* rowid of the last tuple sent */
|
||||
SPITupleTable *spi_tuptable; /* sql results from user query */
|
||||
char *lastrowid; /* rowid of the last tuple sent */
|
||||
} crosstab_fctx;
|
||||
|
||||
#define GET_TEXT(cstrp) DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(cstrp)))
|
||||
@@ -108,23 +108,26 @@ PG_FUNCTION_INFO_V1(normal_rand);
|
||||
Datum
|
||||
normal_rand(PG_FUNCTION_ARGS)
|
||||
{
|
||||
FuncCallContext *funcctx;
|
||||
int call_cntr;
|
||||
int max_calls;
|
||||
normal_rand_fctx *fctx;
|
||||
float8 mean;
|
||||
float8 stddev;
|
||||
float8 carry_val;
|
||||
bool use_carry;
|
||||
MemoryContext oldcontext;
|
||||
FuncCallContext *funcctx;
|
||||
int call_cntr;
|
||||
int max_calls;
|
||||
normal_rand_fctx *fctx;
|
||||
float8 mean;
|
||||
float8 stddev;
|
||||
float8 carry_val;
|
||||
bool use_carry;
|
||||
MemoryContext oldcontext;
|
||||
|
||||
/* stuff done only on the first call of the function */
|
||||
if(SRF_IS_FIRSTCALL())
|
||||
{
|
||||
if (SRF_IS_FIRSTCALL())
|
||||
{
|
||||
/* create a function context for cross-call persistence */
|
||||
funcctx = SRF_FIRSTCALL_INIT();
|
||||
funcctx = SRF_FIRSTCALL_INIT();
|
||||
|
||||
/* switch to memory context appropriate for multiple function calls */
|
||||
/*
|
||||
* switch to memory context appropriate for multiple function
|
||||
* calls
|
||||
*/
|
||||
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
|
||||
|
||||
/* total number of tuples to be returned */
|
||||
@@ -134,11 +137,10 @@ normal_rand(PG_FUNCTION_ARGS)
|
||||
fctx = (normal_rand_fctx *) palloc(sizeof(normal_rand_fctx));
|
||||
|
||||
/*
|
||||
* Use fctx to keep track of upper and lower bounds
|
||||
* from call to call. It will also be used to carry over
|
||||
* the spare value we get from the Box-Muller algorithm
|
||||
* so that we only actually calculate a new value every
|
||||
* other call.
|
||||
* Use fctx to keep track of upper and lower bounds from call to
|
||||
* call. It will also be used to carry over the spare value we get
|
||||
* from the Box-Muller algorithm so that we only actually
|
||||
* calculate a new value every other call.
|
||||
*/
|
||||
fctx->mean = PG_GETARG_FLOAT8(1);
|
||||
fctx->stddev = PG_GETARG_FLOAT8(2);
|
||||
@@ -154,10 +156,10 @@ normal_rand(PG_FUNCTION_ARGS)
|
||||
srandom(PG_GETARG_UINT32(3));
|
||||
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
}
|
||||
}
|
||||
|
||||
/* stuff done on every call of the function */
|
||||
funcctx = SRF_PERCALL_SETUP();
|
||||
funcctx = SRF_PERCALL_SETUP();
|
||||
|
||||
call_cntr = funcctx->call_cntr;
|
||||
max_calls = funcctx->max_calls;
|
||||
@@ -166,12 +168,12 @@ normal_rand(PG_FUNCTION_ARGS)
|
||||
stddev = fctx->stddev;
|
||||
carry_val = fctx->carry_val;
|
||||
use_carry = fctx->use_carry;
|
||||
|
||||
if (call_cntr < max_calls) /* do when there is more left to send */
|
||||
{
|
||||
|
||||
if (call_cntr < max_calls) /* do when there is more left to send */
|
||||
{
|
||||
float8 result;
|
||||
|
||||
if(use_carry)
|
||||
if (use_carry)
|
||||
{
|
||||
/*
|
||||
* reset use_carry and use second value obtained on last pass
|
||||
@@ -196,12 +198,11 @@ normal_rand(PG_FUNCTION_ARGS)
|
||||
}
|
||||
|
||||
/* send the result */
|
||||
SRF_RETURN_NEXT(funcctx, Float8GetDatum(result));
|
||||
}
|
||||
else /* do when there is no more left */
|
||||
{
|
||||
SRF_RETURN_DONE(funcctx);
|
||||
}
|
||||
SRF_RETURN_NEXT(funcctx, Float8GetDatum(result));
|
||||
}
|
||||
else
|
||||
/* do when there is no more left */
|
||||
SRF_RETURN_DONE(funcctx);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -218,9 +219,13 @@ normal_rand(PG_FUNCTION_ARGS)
|
||||
static void
|
||||
get_normal_pair(float8 *x1, float8 *x2)
|
||||
{
|
||||
float8 u1, u2, v1, v2, s;
|
||||
float8 u1,
|
||||
u2,
|
||||
v1,
|
||||
v2,
|
||||
s;
|
||||
|
||||
for(;;)
|
||||
for (;;)
|
||||
{
|
||||
u1 = (float8) random() / (float8) RAND_MAX;
|
||||
u2 = (float8) random() / (float8) RAND_MAX;
|
||||
@@ -257,65 +262,68 @@ get_normal_pair(float8 *x1, float8 *x2)
|
||||
*
|
||||
* rowid cat value
|
||||
* ------+-------+-------
|
||||
* row1 cat1 val1
|
||||
* row1 cat2 val2
|
||||
* row1 cat3 val3
|
||||
* row1 cat4 val4
|
||||
* row2 cat1 val5
|
||||
* row2 cat2 val6
|
||||
* row2 cat3 val7
|
||||
* row2 cat4 val8
|
||||
* row1 cat1 val1
|
||||
* row1 cat2 val2
|
||||
* row1 cat3 val3
|
||||
* row1 cat4 val4
|
||||
* row2 cat1 val5
|
||||
* row2 cat2 val6
|
||||
* row2 cat3 val7
|
||||
* row2 cat4 val8
|
||||
*
|
||||
* crosstab returns:
|
||||
* <===== values columns =====>
|
||||
* rowid cat1 cat2 cat3 cat4
|
||||
* ------+-------+-------+-------+-------
|
||||
* row1 val1 val2 val3 val4
|
||||
* row2 val5 val6 val7 val8
|
||||
* row1 val1 val2 val3 val4
|
||||
* row2 val5 val6 val7 val8
|
||||
*
|
||||
* NOTES:
|
||||
* 1. SQL result must be ordered by 1,2.
|
||||
* 2. The number of values columns depends on the tuple description
|
||||
* of the function's declared return type.
|
||||
* of the function's declared return type.
|
||||
* 2. Missing values (i.e. not enough adjacent rows of same rowid to
|
||||
* fill the number of result values columns) are filled in with nulls.
|
||||
* fill the number of result values columns) are filled in with nulls.
|
||||
* 3. Extra values (i.e. too many adjacent rows of same rowid to fill
|
||||
* the number of result values columns) are skipped.
|
||||
* the number of result values columns) are skipped.
|
||||
* 4. Rows with all nulls in the values columns are skipped.
|
||||
*/
|
||||
PG_FUNCTION_INFO_V1(crosstab);
|
||||
Datum
|
||||
crosstab(PG_FUNCTION_ARGS)
|
||||
{
|
||||
FuncCallContext *funcctx;
|
||||
TupleDesc ret_tupdesc;
|
||||
int call_cntr;
|
||||
int max_calls;
|
||||
TupleTableSlot *slot;
|
||||
AttInMetadata *attinmeta;
|
||||
SPITupleTable *spi_tuptable = NULL;
|
||||
TupleDesc spi_tupdesc;
|
||||
char *lastrowid = NULL;
|
||||
crosstab_fctx *fctx;
|
||||
int i;
|
||||
int num_categories;
|
||||
MemoryContext oldcontext;
|
||||
FuncCallContext *funcctx;
|
||||
TupleDesc ret_tupdesc;
|
||||
int call_cntr;
|
||||
int max_calls;
|
||||
TupleTableSlot *slot;
|
||||
AttInMetadata *attinmeta;
|
||||
SPITupleTable *spi_tuptable = NULL;
|
||||
TupleDesc spi_tupdesc;
|
||||
char *lastrowid = NULL;
|
||||
crosstab_fctx *fctx;
|
||||
int i;
|
||||
int num_categories;
|
||||
MemoryContext oldcontext;
|
||||
|
||||
/* stuff done only on the first call of the function */
|
||||
if(SRF_IS_FIRSTCALL())
|
||||
{
|
||||
char *sql = GET_STR(PG_GETARG_TEXT_P(0));
|
||||
Oid funcid = fcinfo->flinfo->fn_oid;
|
||||
Oid functypeid;
|
||||
char functyptype;
|
||||
TupleDesc tupdesc = NULL;
|
||||
int ret;
|
||||
int proc;
|
||||
if (SRF_IS_FIRSTCALL())
|
||||
{
|
||||
char *sql = GET_STR(PG_GETARG_TEXT_P(0));
|
||||
Oid funcid = fcinfo->flinfo->fn_oid;
|
||||
Oid functypeid;
|
||||
char functyptype;
|
||||
TupleDesc tupdesc = NULL;
|
||||
int ret;
|
||||
int proc;
|
||||
|
||||
/* create a function context for cross-call persistence */
|
||||
funcctx = SRF_FIRSTCALL_INIT();
|
||||
funcctx = SRF_FIRSTCALL_INIT();
|
||||
|
||||
/* switch to memory context appropriate for multiple function calls */
|
||||
/*
|
||||
* switch to memory context appropriate for multiple function
|
||||
* calls
|
||||
*/
|
||||
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
|
||||
|
||||
/* Connect to SPI manager */
|
||||
@@ -336,20 +344,19 @@ crosstab(PG_FUNCTION_ARGS)
|
||||
* The provided SQL query must always return three columns.
|
||||
*
|
||||
* 1. rowname the label or identifier for each row in the final
|
||||
* result
|
||||
* 2. category the label or identifier for each column in the
|
||||
* final result
|
||||
* 3. values the value for each column in the final result
|
||||
* result 2. category the label or identifier for each column
|
||||
* in the final result 3. values the value for each column
|
||||
* in the final result
|
||||
*/
|
||||
if (spi_tupdesc->natts != 3)
|
||||
elog(ERROR, "crosstab: provided SQL must return 3 columns;"
|
||||
" a rowid, a category, and a values column");
|
||||
" a rowid, a category, and a values column");
|
||||
}
|
||||
else
|
||||
{
|
||||
/* no qualifying tuples */
|
||||
SPI_finish();
|
||||
SRF_RETURN_DONE(funcctx);
|
||||
SRF_RETURN_DONE(funcctx);
|
||||
}
|
||||
|
||||
/* SPI switches context on us, so reset it */
|
||||
@@ -360,7 +367,7 @@ crosstab(PG_FUNCTION_ARGS)
|
||||
|
||||
/* check typtype to see if we have a predetermined return type */
|
||||
functyptype = get_typtype(functypeid);
|
||||
|
||||
|
||||
if (functyptype == 'c')
|
||||
{
|
||||
/* Build a tuple description for a functypeid tuple */
|
||||
@@ -372,7 +379,7 @@ crosstab(PG_FUNCTION_ARGS)
|
||||
elog(ERROR, "Wrong number of arguments specified for function");
|
||||
else
|
||||
{
|
||||
int num_catagories = PG_GETARG_INT32(1);
|
||||
int num_catagories = PG_GETARG_INT32(1);
|
||||
|
||||
tupdesc = make_crosstab_tupledesc(spi_tupdesc, num_catagories);
|
||||
}
|
||||
@@ -389,7 +396,7 @@ crosstab(PG_FUNCTION_ARGS)
|
||||
*/
|
||||
if (!compatCrosstabTupleDescs(tupdesc, spi_tupdesc))
|
||||
elog(ERROR, "crosstab: return and sql tuple descriptions are"
|
||||
" incompatible");
|
||||
" incompatible");
|
||||
|
||||
/* allocate a slot for a tuple with this tupdesc */
|
||||
slot = TupleDescGetSlot(tupdesc);
|
||||
@@ -398,8 +405,8 @@ crosstab(PG_FUNCTION_ARGS)
|
||||
funcctx->slot = slot;
|
||||
|
||||
/*
|
||||
* Generate attribute metadata needed later to produce tuples from raw
|
||||
* C strings
|
||||
* Generate attribute metadata needed later to produce tuples from
|
||||
* raw C strings
|
||||
*/
|
||||
attinmeta = TupleDescGetAttInMetadata(tupdesc);
|
||||
funcctx->attinmeta = attinmeta;
|
||||
@@ -418,10 +425,10 @@ crosstab(PG_FUNCTION_ARGS)
|
||||
funcctx->max_calls = proc;
|
||||
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
}
|
||||
}
|
||||
|
||||
/* stuff done on every call of the function */
|
||||
funcctx = SRF_PERCALL_SETUP();
|
||||
funcctx = SRF_PERCALL_SETUP();
|
||||
|
||||
/*
|
||||
* initialize per-call variables
|
||||
@@ -446,9 +453,9 @@ crosstab(PG_FUNCTION_ARGS)
|
||||
|
||||
/* the return tuple always must have 1 rowid + num_categories columns */
|
||||
num_categories = ret_tupdesc->natts - 1;
|
||||
|
||||
|
||||
if (call_cntr < max_calls) /* do when there is more left to send */
|
||||
{
|
||||
{
|
||||
HeapTuple tuple;
|
||||
Datum result;
|
||||
char **values;
|
||||
@@ -463,8 +470,8 @@ crosstab(PG_FUNCTION_ARGS)
|
||||
memset(values, '\0', (1 + num_categories) * sizeof(char *));
|
||||
|
||||
/*
|
||||
* now loop through the sql results and assign each value
|
||||
* in sequence to the next category
|
||||
* now loop through the sql results and assign each value in
|
||||
* sequence to the next category
|
||||
*/
|
||||
for (i = 0; i < num_categories; i++)
|
||||
{
|
||||
@@ -481,11 +488,12 @@ crosstab(PG_FUNCTION_ARGS)
|
||||
/* get the rowid from the current sql result tuple */
|
||||
rowid = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
|
||||
|
||||
/*
|
||||
* If this is the first pass through the values for this rowid
|
||||
* set it, otherwise make sure it hasn't changed on us. Also
|
||||
* check to see if the rowid is the same as that of the last
|
||||
* tuple sent -- if so, skip this tuple entirely
|
||||
/*
|
||||
* If this is the first pass through the values for this
|
||||
* rowid set it, otherwise make sure it hasn't changed on
|
||||
* us. Also check to see if the rowid is the same as that
|
||||
* of the last tuple sent -- if so, skip this tuple
|
||||
* entirely
|
||||
*/
|
||||
if (i == 0)
|
||||
values[0] = pstrdup(rowid);
|
||||
@@ -498,18 +506,19 @@ crosstab(PG_FUNCTION_ARGS)
|
||||
allnulls = false;
|
||||
|
||||
/*
|
||||
* Get the next category item value, which is alway attribute
|
||||
* number three.
|
||||
* Get the next category item value, which is alway
|
||||
* attribute number three.
|
||||
*
|
||||
* Be careful to sssign the value to the array index based
|
||||
* on which category we are presently processing.
|
||||
* Be careful to sssign the value to the array index
|
||||
* based on which category we are presently
|
||||
* processing.
|
||||
*/
|
||||
values[1 + i] = SPI_getvalue(spi_tuple, spi_tupdesc, 3);
|
||||
|
||||
/*
|
||||
* increment the counter since we consume a row
|
||||
* for each category, but not for last pass
|
||||
* because the API will do that for us
|
||||
* increment the counter since we consume a row for
|
||||
* each category, but not for last pass because the
|
||||
* API will do that for us
|
||||
*/
|
||||
if (i < (num_categories - 1))
|
||||
call_cntr = ++funcctx->call_cntr;
|
||||
@@ -517,10 +526,9 @@ crosstab(PG_FUNCTION_ARGS)
|
||||
else
|
||||
{
|
||||
/*
|
||||
* We'll fill in NULLs for the missing values,
|
||||
* but we need to decrement the counter since
|
||||
* this sql result row doesn't belong to the current
|
||||
* output tuple.
|
||||
* We'll fill in NULLs for the missing values, but we
|
||||
* need to decrement the counter since this sql result
|
||||
* row doesn't belong to the current output tuple.
|
||||
*/
|
||||
call_cntr = --funcctx->call_cntr;
|
||||
break;
|
||||
@@ -534,7 +542,10 @@ crosstab(PG_FUNCTION_ARGS)
|
||||
|
||||
if (values[0] != NULL)
|
||||
{
|
||||
/* switch to memory context appropriate for multiple function calls */
|
||||
/*
|
||||
* switch to memory context appropriate for multiple
|
||||
* function calls
|
||||
*/
|
||||
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
|
||||
|
||||
lastrowid = fctx->lastrowid = pstrdup(values[0]);
|
||||
@@ -555,14 +566,13 @@ crosstab(PG_FUNCTION_ARGS)
|
||||
xpfree(values[i]);
|
||||
xpfree(values);
|
||||
|
||||
SRF_RETURN_NEXT(funcctx, result);
|
||||
SRF_RETURN_NEXT(funcctx, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Skipping this tuple entirely, but we need to advance
|
||||
* the counter like the API would if we had returned
|
||||
* one.
|
||||
* the counter like the API would if we had returned one.
|
||||
*/
|
||||
call_cntr = ++funcctx->call_cntr;
|
||||
|
||||
@@ -574,17 +584,18 @@ crosstab(PG_FUNCTION_ARGS)
|
||||
{
|
||||
/* release SPI related resources */
|
||||
SPI_finish();
|
||||
SRF_RETURN_DONE(funcctx);
|
||||
SRF_RETURN_DONE(funcctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else /* do when there is no more left */
|
||||
{
|
||||
else
|
||||
/* do when there is no more left */
|
||||
{
|
||||
/* release SPI related resources */
|
||||
SPI_finish();
|
||||
SRF_RETURN_DONE(funcctx);
|
||||
}
|
||||
SRF_RETURN_DONE(funcctx);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -595,29 +606,29 @@ crosstab(PG_FUNCTION_ARGS)
|
||||
*
|
||||
* keyid parent_keyid
|
||||
* ------+--------------
|
||||
* row1 NULL
|
||||
* row2 row1
|
||||
* row3 row1
|
||||
* row4 row2
|
||||
* row5 row2
|
||||
* row6 row4
|
||||
* row7 row3
|
||||
* row8 row6
|
||||
* row9 row5
|
||||
* row1 NULL
|
||||
* row2 row1
|
||||
* row3 row1
|
||||
* row4 row2
|
||||
* row5 row2
|
||||
* row6 row4
|
||||
* row7 row3
|
||||
* row8 row6
|
||||
* row9 row5
|
||||
*
|
||||
*
|
||||
* connectby(text relname, text keyid_fld, text parent_keyid_fld,
|
||||
* text start_with, int max_depth [, text branch_delim])
|
||||
* text start_with, int max_depth [, text branch_delim])
|
||||
* connectby('foo', 'keyid', 'parent_keyid', 'row2', 0, '~') returns:
|
||||
*
|
||||
* keyid parent_id level branch
|
||||
* ------+-----------+--------+-----------------------
|
||||
* row2 NULL 0 row2
|
||||
* row4 row2 1 row2~row4
|
||||
* row6 row4 2 row2~row4~row6
|
||||
* row8 row6 3 row2~row4~row6~row8
|
||||
* row5 row2 1 row2~row5
|
||||
* row9 row5 2 row2~row5~row9
|
||||
* row2 NULL 0 row2
|
||||
* row4 row2 1 row2~row4
|
||||
* row6 row4 2 row2~row4~row6
|
||||
* row8 row6 3 row2~row4~row6~row8
|
||||
* row5 row2 1 row2~row5
|
||||
* row9 row5 2 row2~row5~row9
|
||||
*
|
||||
*/
|
||||
PG_FUNCTION_INFO_V1(connectby_text);
|
||||
@@ -628,18 +639,18 @@ PG_FUNCTION_INFO_V1(connectby_text);
|
||||
Datum
|
||||
connectby_text(PG_FUNCTION_ARGS)
|
||||
{
|
||||
char *relname = GET_STR(PG_GETARG_TEXT_P(0));
|
||||
char *key_fld = GET_STR(PG_GETARG_TEXT_P(1));
|
||||
char *parent_key_fld = GET_STR(PG_GETARG_TEXT_P(2));
|
||||
char *start_with = GET_STR(PG_GETARG_TEXT_P(3));
|
||||
int max_depth = PG_GETARG_INT32(4);
|
||||
char *branch_delim = NULL;
|
||||
bool show_branch = false;
|
||||
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
||||
TupleDesc tupdesc;
|
||||
AttInMetadata *attinmeta;
|
||||
MemoryContext per_query_ctx;
|
||||
MemoryContext oldcontext;
|
||||
char *relname = GET_STR(PG_GETARG_TEXT_P(0));
|
||||
char *key_fld = GET_STR(PG_GETARG_TEXT_P(1));
|
||||
char *parent_key_fld = GET_STR(PG_GETARG_TEXT_P(2));
|
||||
char *start_with = GET_STR(PG_GETARG_TEXT_P(3));
|
||||
int max_depth = PG_GETARG_INT32(4);
|
||||
char *branch_delim = NULL;
|
||||
bool show_branch = false;
|
||||
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
||||
TupleDesc tupdesc;
|
||||
AttInMetadata *attinmeta;
|
||||
MemoryContext per_query_ctx;
|
||||
MemoryContext oldcontext;
|
||||
|
||||
if (fcinfo->nargs == 6)
|
||||
{
|
||||
@@ -662,7 +673,7 @@ connectby_text(PG_FUNCTION_ARGS)
|
||||
/* check to see if caller supports us returning a tuplestore */
|
||||
if (!rsinfo->allowedModes & SFRM_Materialize)
|
||||
elog(ERROR, "connectby requires Materialize mode, but it is not "
|
||||
"allowed in this context");
|
||||
"allowed in this context");
|
||||
|
||||
/* OK, go to work */
|
||||
rsinfo->returnMode = SFRM_Materialize;
|
||||
@@ -680,8 +691,8 @@ connectby_text(PG_FUNCTION_ARGS)
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
|
||||
/*
|
||||
* SFRM_Materialize mode expects us to return a NULL Datum.
|
||||
* The actual tuples are in our tuplestore and passed back through
|
||||
* SFRM_Materialize mode expects us to return a NULL Datum. The actual
|
||||
* tuples are in our tuplestore and passed back through
|
||||
* rsinfo->setResult. rsinfo->setDesc is set to the tuple description
|
||||
* that we actually used to build our tuples with, so the caller can
|
||||
* verify we did what it was expecting.
|
||||
@@ -703,9 +714,9 @@ connectby(char *relname,
|
||||
MemoryContext per_query_ctx,
|
||||
AttInMetadata *attinmeta)
|
||||
{
|
||||
Tuplestorestate *tupstore = NULL;
|
||||
int ret;
|
||||
MemoryContext oldcontext;
|
||||
Tuplestorestate *tupstore = NULL;
|
||||
int ret;
|
||||
MemoryContext oldcontext;
|
||||
|
||||
/* Connect to SPI manager */
|
||||
if ((ret = SPI_connect()) < 0)
|
||||
@@ -721,17 +732,17 @@ connectby(char *relname,
|
||||
|
||||
/* now go get the whole tree */
|
||||
tupstore = build_tuplestore_recursively(key_fld,
|
||||
parent_key_fld,
|
||||
relname,
|
||||
branch_delim,
|
||||
start_with,
|
||||
start_with, /* current_branch */
|
||||
0, /* initial level is 0 */
|
||||
max_depth,
|
||||
show_branch,
|
||||
per_query_ctx,
|
||||
attinmeta,
|
||||
tupstore);
|
||||
parent_key_fld,
|
||||
relname,
|
||||
branch_delim,
|
||||
start_with,
|
||||
start_with, /* current_branch */
|
||||
0, /* initial level is 0 */
|
||||
max_depth,
|
||||
show_branch,
|
||||
per_query_ctx,
|
||||
attinmeta,
|
||||
tupstore);
|
||||
|
||||
SPI_finish();
|
||||
|
||||
@@ -756,23 +767,23 @@ build_tuplestore_recursively(char *key_fld,
|
||||
AttInMetadata *attinmeta,
|
||||
Tuplestorestate *tupstore)
|
||||
{
|
||||
TupleDesc tupdesc = attinmeta->tupdesc;
|
||||
MemoryContext oldcontext;
|
||||
StringInfo sql = makeStringInfo();
|
||||
int ret;
|
||||
int proc;
|
||||
TupleDesc tupdesc = attinmeta->tupdesc;
|
||||
MemoryContext oldcontext;
|
||||
StringInfo sql = makeStringInfo();
|
||||
int ret;
|
||||
int proc;
|
||||
|
||||
if(max_depth > 0 && level > max_depth)
|
||||
if (max_depth > 0 && level > max_depth)
|
||||
return tupstore;
|
||||
|
||||
/* Build initial sql statement */
|
||||
appendStringInfo(sql, "SELECT %s, %s FROM %s WHERE %s = '%s' AND %s IS NOT NULL",
|
||||
quote_ident_cstr(key_fld),
|
||||
quote_ident_cstr(parent_key_fld),
|
||||
quote_ident_cstr(relname),
|
||||
quote_ident_cstr(parent_key_fld),
|
||||
start_with,
|
||||
quote_ident_cstr(key_fld));
|
||||
quote_ident_cstr(key_fld),
|
||||
quote_ident_cstr(parent_key_fld),
|
||||
quote_ident_cstr(relname),
|
||||
quote_ident_cstr(parent_key_fld),
|
||||
start_with,
|
||||
quote_ident_cstr(key_fld));
|
||||
|
||||
/* Retrieve the desired rows */
|
||||
ret = SPI_exec(sql->data, 0);
|
||||
@@ -781,16 +792,16 @@ build_tuplestore_recursively(char *key_fld,
|
||||
/* Check for qualifying tuples */
|
||||
if ((ret == SPI_OK_SELECT) && (proc > 0))
|
||||
{
|
||||
HeapTuple tuple;
|
||||
HeapTuple spi_tuple;
|
||||
SPITupleTable *tuptable = SPI_tuptable;
|
||||
TupleDesc spi_tupdesc = tuptable->tupdesc;
|
||||
int i;
|
||||
char *current_key;
|
||||
char *current_key_parent;
|
||||
char current_level[INT32_STRLEN];
|
||||
char *current_branch;
|
||||
char **values;
|
||||
HeapTuple tuple;
|
||||
HeapTuple spi_tuple;
|
||||
SPITupleTable *tuptable = SPI_tuptable;
|
||||
TupleDesc spi_tupdesc = tuptable->tupdesc;
|
||||
int i;
|
||||
char *current_key;
|
||||
char *current_key_parent;
|
||||
char current_level[INT32_STRLEN];
|
||||
char *current_branch;
|
||||
char **values;
|
||||
|
||||
if (show_branch)
|
||||
values = (char **) palloc(CONNECTBY_NCOLS * sizeof(char *));
|
||||
@@ -802,13 +813,13 @@ build_tuplestore_recursively(char *key_fld,
|
||||
{
|
||||
/*
|
||||
* Check that return tupdesc is compatible with the one we got
|
||||
* from the query, but only at level 0 -- no need to check more
|
||||
* than once
|
||||
* from the query, but only at level 0 -- no need to check
|
||||
* more than once
|
||||
*/
|
||||
|
||||
if (!compatConnectbyTupleDescs(tupdesc, spi_tupdesc))
|
||||
elog(ERROR, "connectby: return and sql tuple descriptions are "
|
||||
"incompatible");
|
||||
"incompatible");
|
||||
|
||||
/* root value is the one we initially start with */
|
||||
values[0] = start_with;
|
||||
@@ -842,7 +853,7 @@ build_tuplestore_recursively(char *key_fld,
|
||||
|
||||
for (i = 0; i < proc; i++)
|
||||
{
|
||||
StringInfo branchstr = NULL;
|
||||
StringInfo branchstr = NULL;
|
||||
|
||||
/* start a new branch */
|
||||
if (show_branch)
|
||||
@@ -895,17 +906,17 @@ build_tuplestore_recursively(char *key_fld,
|
||||
|
||||
/* recurse using current_key_parent as the new start_with */
|
||||
tupstore = build_tuplestore_recursively(key_fld,
|
||||
parent_key_fld,
|
||||
relname,
|
||||
branch_delim,
|
||||
values[0],
|
||||
current_branch,
|
||||
level + 1,
|
||||
max_depth,
|
||||
show_branch,
|
||||
per_query_ctx,
|
||||
attinmeta,
|
||||
tupstore);
|
||||
parent_key_fld,
|
||||
relname,
|
||||
branch_delim,
|
||||
values[0],
|
||||
current_branch,
|
||||
level + 1,
|
||||
max_depth,
|
||||
show_branch,
|
||||
per_query_ctx,
|
||||
attinmeta,
|
||||
tupstore);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -923,29 +934,29 @@ validateConnectbyTupleDesc(TupleDesc tupdesc, bool show_branch)
|
||||
{
|
||||
if (tupdesc->natts != CONNECTBY_NCOLS)
|
||||
elog(ERROR, "Query-specified return tuple not valid for Connectby: "
|
||||
"wrong number of columns");
|
||||
"wrong number of columns");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tupdesc->natts != CONNECTBY_NCOLS_NOBRANCH)
|
||||
elog(ERROR, "Query-specified return tuple not valid for Connectby: "
|
||||
"wrong number of columns");
|
||||
"wrong number of columns");
|
||||
}
|
||||
|
||||
/* check that the types of the first two columns match */
|
||||
if (tupdesc->attrs[0]->atttypid != tupdesc->attrs[1]->atttypid)
|
||||
elog(ERROR, "Query-specified return tuple not valid for Connectby: "
|
||||
"first two columns must be the same type");
|
||||
"first two columns must be the same type");
|
||||
|
||||
/* check that the type of the third column is INT4 */
|
||||
if (tupdesc->attrs[2]->atttypid != INT4OID)
|
||||
elog(ERROR, "Query-specified return tuple not valid for Connectby: "
|
||||
"third column must be type %s", format_type_be(INT4OID));
|
||||
"third column must be type %s", format_type_be(INT4OID));
|
||||
|
||||
/* check that the type of the forth column is TEXT if applicable */
|
||||
if (show_branch && tupdesc->attrs[3]->atttypid != TEXTOID)
|
||||
elog(ERROR, "Query-specified return tuple not valid for Connectby: "
|
||||
"third column must be type %s", format_type_be(TEXTOID));
|
||||
"third column must be type %s", format_type_be(TEXTOID));
|
||||
|
||||
/* OK, the tupdesc is valid for our purposes */
|
||||
}
|
||||
@@ -956,22 +967,22 @@ validateConnectbyTupleDesc(TupleDesc tupdesc, bool show_branch)
|
||||
static bool
|
||||
compatConnectbyTupleDescs(TupleDesc ret_tupdesc, TupleDesc sql_tupdesc)
|
||||
{
|
||||
Oid ret_atttypid;
|
||||
Oid sql_atttypid;
|
||||
Oid ret_atttypid;
|
||||
Oid sql_atttypid;
|
||||
|
||||
/* check the key_fld types match */
|
||||
ret_atttypid = ret_tupdesc->attrs[0]->atttypid;
|
||||
sql_atttypid = sql_tupdesc->attrs[0]->atttypid;
|
||||
if (ret_atttypid != sql_atttypid)
|
||||
elog(ERROR, "compatConnectbyTupleDescs: SQL key field datatype does "
|
||||
"not match return key field datatype");
|
||||
"not match return key field datatype");
|
||||
|
||||
/* check the parent_key_fld types match */
|
||||
ret_atttypid = ret_tupdesc->attrs[1]->atttypid;
|
||||
sql_atttypid = sql_tupdesc->attrs[1]->atttypid;
|
||||
if (ret_atttypid != sql_atttypid)
|
||||
elog(ERROR, "compatConnectbyTupleDescs: SQL parent key field datatype "
|
||||
"does not match return parent key field datatype");
|
||||
"does not match return parent key field datatype");
|
||||
|
||||
/* OK, the two tupdescs are compatible for our purposes */
|
||||
return true;
|
||||
@@ -984,23 +995,22 @@ static bool
|
||||
compatCrosstabTupleDescs(TupleDesc ret_tupdesc, TupleDesc sql_tupdesc)
|
||||
{
|
||||
int i;
|
||||
Form_pg_attribute ret_attr;
|
||||
Oid ret_atttypid;
|
||||
Form_pg_attribute sql_attr;
|
||||
Oid sql_atttypid;
|
||||
Form_pg_attribute ret_attr;
|
||||
Oid ret_atttypid;
|
||||
Form_pg_attribute sql_attr;
|
||||
Oid sql_atttypid;
|
||||
|
||||
/* check the rowid types match */
|
||||
ret_atttypid = ret_tupdesc->attrs[0]->atttypid;
|
||||
sql_atttypid = sql_tupdesc->attrs[0]->atttypid;
|
||||
if (ret_atttypid != sql_atttypid)
|
||||
elog(ERROR, "compatCrosstabTupleDescs: SQL rowid datatype does not match"
|
||||
" return rowid datatype");
|
||||
" return rowid datatype");
|
||||
|
||||
/*
|
||||
* - attribute [1] of the sql tuple is the category;
|
||||
* no need to check it
|
||||
* - attribute [2] of the sql tuple should match
|
||||
* attributes [1] to [natts] of the return tuple
|
||||
* - attribute [1] of the sql tuple is the category; no need to check
|
||||
* it - attribute [2] of the sql tuple should match attributes [1] to
|
||||
* [natts] of the return tuple
|
||||
*/
|
||||
sql_attr = sql_tupdesc->attrs[2];
|
||||
for (i = 1; i < ret_tupdesc->natts; i++)
|
||||
@@ -1018,19 +1028,18 @@ compatCrosstabTupleDescs(TupleDesc ret_tupdesc, TupleDesc sql_tupdesc)
|
||||
static TupleDesc
|
||||
make_crosstab_tupledesc(TupleDesc spi_tupdesc, int num_catagories)
|
||||
{
|
||||
Form_pg_attribute sql_attr;
|
||||
Oid sql_atttypid;
|
||||
TupleDesc tupdesc;
|
||||
int natts;
|
||||
AttrNumber attnum;
|
||||
char attname[NAMEDATALEN];
|
||||
int i;
|
||||
Form_pg_attribute sql_attr;
|
||||
Oid sql_atttypid;
|
||||
TupleDesc tupdesc;
|
||||
int natts;
|
||||
AttrNumber attnum;
|
||||
char attname[NAMEDATALEN];
|
||||
int i;
|
||||
|
||||
/*
|
||||
* We need to build a tuple description with one column
|
||||
* for the rowname, and num_catagories columns for the values.
|
||||
* Each must be of the same type as the corresponding
|
||||
* spi result input column.
|
||||
* We need to build a tuple description with one column for the
|
||||
* rowname, and num_catagories columns for the values. Each must be of
|
||||
* the same type as the corresponding spi result input column.
|
||||
*/
|
||||
natts = num_catagories + 1;
|
||||
tupdesc = CreateTemplateTupleDesc(natts, false);
|
||||
@@ -1069,9 +1078,9 @@ make_crosstab_tupledesc(TupleDesc spi_tupdesc, int num_catagories)
|
||||
static char *
|
||||
quote_ident_cstr(char *rawstr)
|
||||
{
|
||||
text *rawstr_text;
|
||||
text *result_text;
|
||||
char *result;
|
||||
text *rawstr_text;
|
||||
text *result_text;
|
||||
char *result;
|
||||
|
||||
rawstr_text = DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(rawstr)));
|
||||
result_text = DatumGetTextP(DirectFunctionCall1(quote_ident, PointerGetDatum(rawstr_text)));
|
||||
|
@@ -11,13 +11,13 @@
|
||||
* documentation for any purpose, without fee, and without a written agreement
|
||||
* is hereby granted, provided that the above copyright notice and this
|
||||
* paragraph and the following two paragraphs appear in all copies.
|
||||
*
|
||||
*
|
||||
* IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
|
||||
* LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
|
||||
* DOCUMENTATION, EVEN IF THE AUTHOR OR DISTRIBUTORS HAVE BEEN ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*
|
||||
* THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
|
@@ -12,12 +12,12 @@
|
||||
#define HOST 6
|
||||
#define SCIENTIFIC 7
|
||||
#define VERSIONNUMBER 8
|
||||
#define PARTHYPHENWORD 9
|
||||
#define CYRPARTHYPHENWORD 10
|
||||
#define LATPARTHYPHENWORD 11
|
||||
#define SPACE 12
|
||||
#define TAG 13
|
||||
#define HTTP 14
|
||||
#define PARTHYPHENWORD 9
|
||||
#define CYRPARTHYPHENWORD 10
|
||||
#define LATPARTHYPHENWORD 11
|
||||
#define SPACE 12
|
||||
#define TAG 13
|
||||
#define HTTP 14
|
||||
#define HYPHENWORD 15
|
||||
#define LATHYPHENWORD 16
|
||||
#define CYRHYPHENWORD 17
|
||||
@@ -25,10 +25,9 @@
|
||||
#define FILEPATH 19
|
||||
#define DECIMAL 20
|
||||
#define SIGNEDINT 21
|
||||
#define UNSIGNEDINT 22
|
||||
#define UNSIGNEDINT 22
|
||||
#define HTMLENTITY 23
|
||||
|
||||
extern const char *descr[];
|
||||
|
||||
#endif
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -114,7 +114,7 @@ initmorph(void)
|
||||
needinit[i] = false;
|
||||
|
||||
PGLC_current(&lc);
|
||||
if ( lc.lc_ctype )
|
||||
if (lc.lc_ctype)
|
||||
for (i = 1; i < lengthof(dicts); i++)
|
||||
if (strcmp(dicts[i].localename, lc.lc_ctype) == 0)
|
||||
{
|
||||
|
@@ -593,8 +593,8 @@ mqtxt_in(PG_FUNCTION_ARGS)
|
||||
res = clean_fakeval(GETQUERY(query), &len);
|
||||
if (!res)
|
||||
{
|
||||
query->len=HDRSIZEQT;
|
||||
query->size=0;
|
||||
query->len = HDRSIZEQT;
|
||||
query->size = 0;
|
||||
PG_RETURN_POINTER(query);
|
||||
}
|
||||
memcpy((void *) GETQUERY(query), (void *) res, len * sizeof(ITEM));
|
||||
@@ -739,11 +739,13 @@ qtxt_out(PG_FUNCTION_ARGS)
|
||||
QUERYTYPE *query = (QUERYTYPE *) DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(0)));
|
||||
INFIX nrm;
|
||||
|
||||
if (query->size == 0) {
|
||||
char *b=palloc(1);
|
||||
*b='\0';
|
||||
if (query->size == 0)
|
||||
{
|
||||
char *b = palloc(1);
|
||||
|
||||
*b = '\0';
|
||||
PG_RETURN_POINTER(b);
|
||||
}
|
||||
}
|
||||
nrm.curpol = GETQUERY(query);
|
||||
nrm.buflen = 32;
|
||||
nrm.cur = nrm.buf = (char *) palloc(sizeof(char) * nrm.buflen);
|
||||
@@ -769,11 +771,12 @@ querytree(PG_FUNCTION_ARGS)
|
||||
int4 len;
|
||||
|
||||
|
||||
if (query->size == 0) {
|
||||
if (query->size == 0)
|
||||
{
|
||||
res = (text *) palloc(VARHDRSZ);
|
||||
VARATT_SIZEP(res) = VARHDRSZ;
|
||||
PG_RETURN_POINTER(res);
|
||||
}
|
||||
}
|
||||
|
||||
q = clean_NOT(GETQUERY(query), &len);
|
||||
|
||||
|
@@ -327,7 +327,7 @@ typedef struct
|
||||
{
|
||||
uint16 len;
|
||||
char *word;
|
||||
} WORD;
|
||||
} WORD;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@@ -402,7 +402,7 @@ compareWORD(const void *a, const void *b)
|
||||
}
|
||||
|
||||
static int
|
||||
uniqueWORD(WORD *a, int4 l)
|
||||
uniqueWORD(WORD * a, int4 l)
|
||||
{
|
||||
WORD *ptr,
|
||||
*res;
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/contrib/vacuumlo/vacuumlo.c,v 1.13 2002/08/15 02:58:29 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/contrib/vacuumlo/vacuumlo.c,v 1.14 2002/09/04 20:31:08 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -35,20 +35,23 @@
|
||||
#define BUFSIZE 1024
|
||||
|
||||
extern char *optarg;
|
||||
extern int optind, opterr, optopt;
|
||||
extern int optind,
|
||||
opterr,
|
||||
optopt;
|
||||
|
||||
struct _param {
|
||||
char *pg_user;
|
||||
int pg_prompt;
|
||||
char *pg_port;
|
||||
char *pg_host;
|
||||
int verbose;
|
||||
int dry_run;
|
||||
struct _param
|
||||
{
|
||||
char *pg_user;
|
||||
int pg_prompt;
|
||||
char *pg_port;
|
||||
char *pg_host;
|
||||
int verbose;
|
||||
int dry_run;
|
||||
};
|
||||
|
||||
int vacuumlo(char *, struct _param *);
|
||||
char *simple_prompt(const char *prompt, int , int);
|
||||
void usage(void);
|
||||
int vacuumlo(char *, struct _param *);
|
||||
char *simple_prompt(const char *prompt, int, int);
|
||||
void usage(void);
|
||||
|
||||
|
||||
/*
|
||||
@@ -63,7 +66,7 @@ void usage(void);
|
||||
*
|
||||
* Returns a malloc()'ed string with the input (w/o trailing newline).
|
||||
*/
|
||||
static int prompt_state = 0;
|
||||
static int prompt_state = 0;
|
||||
|
||||
char *
|
||||
simple_prompt(const char *prompt, int maxlen, int echo)
|
||||
@@ -82,7 +85,7 @@ simple_prompt(const char *prompt, int maxlen, int echo)
|
||||
if (!destination)
|
||||
return NULL;
|
||||
|
||||
prompt_state = 1; /* disable SIGINT */
|
||||
prompt_state = 1; /* disable SIGINT */
|
||||
|
||||
/*
|
||||
* Do not try to collapse these into one "w+" mode file. Doesn't work
|
||||
@@ -153,7 +156,7 @@ simple_prompt(const char *prompt, int maxlen, int echo)
|
||||
fclose(termout);
|
||||
}
|
||||
|
||||
prompt_state = 0; /* SIGINT okay again */
|
||||
prompt_state = 0; /* SIGINT okay again */
|
||||
|
||||
return destination;
|
||||
}
|
||||
@@ -164,33 +167,35 @@ simple_prompt(const char *prompt, int maxlen, int echo)
|
||||
* This vacuums LOs of one database. It returns 0 on success, -1 on failure.
|
||||
*/
|
||||
int
|
||||
vacuumlo(char *database, struct _param *param)
|
||||
vacuumlo(char *database, struct _param * param)
|
||||
{
|
||||
PGconn *conn;
|
||||
PGresult *res,
|
||||
*res2;
|
||||
*res2;
|
||||
char buf[BUFSIZE];
|
||||
int matched;
|
||||
int deleted;
|
||||
int i;
|
||||
char *password = NULL;
|
||||
int matched;
|
||||
int deleted;
|
||||
int i;
|
||||
char *password = NULL;
|
||||
|
||||
if(param->pg_prompt) {
|
||||
password = simple_prompt("Password: ", 32, 0);
|
||||
if(!password) {
|
||||
fprintf(stderr, "failed to get password\n");
|
||||
exit(1);
|
||||
if (param->pg_prompt)
|
||||
{
|
||||
password = simple_prompt("Password: ", 32, 0);
|
||||
if (!password)
|
||||
{
|
||||
fprintf(stderr, "failed to get password\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
conn = PQsetdbLogin( param->pg_host,
|
||||
param->pg_port,
|
||||
NULL,
|
||||
NULL,
|
||||
database,
|
||||
param->pg_user,
|
||||
password
|
||||
);
|
||||
conn = PQsetdbLogin(param->pg_host,
|
||||
param->pg_port,
|
||||
NULL,
|
||||
NULL,
|
||||
database,
|
||||
param->pg_user,
|
||||
password
|
||||
);
|
||||
|
||||
/* check to see that the backend connection was successfully made */
|
||||
if (PQstatus(conn) == CONNECTION_BAD)
|
||||
@@ -201,10 +206,11 @@ vacuumlo(char *database, struct _param *param)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (param->verbose) {
|
||||
if (param->verbose)
|
||||
{
|
||||
fprintf(stdout, "Connected to %s\n", database);
|
||||
if(param->dry_run)
|
||||
fprintf(stdout, "Test run: no large objects will be removed!\n");
|
||||
if (param->dry_run)
|
||||
fprintf(stdout, "Test run: no large objects will be removed!\n");
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -289,7 +295,7 @@ vacuumlo(char *database, struct _param *param)
|
||||
* whole program is a Postgres-ism.
|
||||
*/
|
||||
snprintf(buf, BUFSIZE, "DELETE FROM vacuum_l WHERE lo = \"%s\".\"%s\" ",
|
||||
table, field);
|
||||
table, field);
|
||||
res2 = PQexec(conn, buf);
|
||||
if (PQresultStatus(res2) != PGRES_COMMAND_OK)
|
||||
{
|
||||
@@ -342,16 +348,18 @@ vacuumlo(char *database, struct _param *param)
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
if(param->dry_run == 0) {
|
||||
if (lo_unlink(conn, lo) < 0)
|
||||
{
|
||||
fprintf(stderr, "\nFailed to remove lo %u: ", lo);
|
||||
fprintf(stderr, "%s", PQerrorMessage(conn));
|
||||
}
|
||||
else
|
||||
deleted++;
|
||||
} else
|
||||
deleted++;
|
||||
if (param->dry_run == 0)
|
||||
{
|
||||
if (lo_unlink(conn, lo) < 0)
|
||||
{
|
||||
fprintf(stderr, "\nFailed to remove lo %u: ", lo);
|
||||
fprintf(stderr, "%s", PQerrorMessage(conn));
|
||||
}
|
||||
else
|
||||
deleted++;
|
||||
}
|
||||
else
|
||||
deleted++;
|
||||
}
|
||||
PQclear(res);
|
||||
|
||||
@@ -365,23 +373,24 @@ vacuumlo(char *database, struct _param *param)
|
||||
|
||||
if (param->verbose)
|
||||
fprintf(stdout, "\r%s %d large objects from %s.\n",
|
||||
(param->dry_run?"Would remove":"Removed"), deleted, database);
|
||||
(param->dry_run ? "Would remove" : "Removed"), deleted, database);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
usage(void) {
|
||||
fprintf(stdout, "vacuumlo removes unreferenced large objects from databases\n\n");
|
||||
fprintf(stdout, "Usage:\n vacuumlo [options] dbname [dbnames...]\n\n");
|
||||
fprintf(stdout, "Options:\n");
|
||||
usage(void)
|
||||
{
|
||||
fprintf(stdout, "vacuumlo removes unreferenced large objects from databases\n\n");
|
||||
fprintf(stdout, "Usage:\n vacuumlo [options] dbname [dbnames...]\n\n");
|
||||
fprintf(stdout, "Options:\n");
|
||||
fprintf(stdout, " -v\t\tWrite a lot of output\n");
|
||||
fprintf(stdout, " -n\t\tDon't remove any large object, just show what would be done\n");
|
||||
fprintf(stdout, " -U username\tUsername to connect as\n");
|
||||
fprintf(stdout, " -W\t\tPrompt for password\n");
|
||||
fprintf(stdout, " -h hostname\tDatabase server host\n");
|
||||
fprintf(stdout, " -U username\tUsername to connect as\n");
|
||||
fprintf(stdout, " -W\t\tPrompt for password\n");
|
||||
fprintf(stdout, " -h hostname\tDatabase server host\n");
|
||||
fprintf(stdout, " -p port\tDatabase server port\n");
|
||||
fprintf(stdout, " -p port\tDatabase server port\n\n");
|
||||
fprintf(stdout, " -p port\tDatabase server port\n\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -389,69 +398,75 @@ int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int rc = 0;
|
||||
struct _param param;
|
||||
int c;
|
||||
int port;
|
||||
struct _param param;
|
||||
int c;
|
||||
int port;
|
||||
|
||||
/* Parameter handling */
|
||||
param.pg_user = NULL;
|
||||
param.pg_user = NULL;
|
||||
param.pg_prompt = 0;
|
||||
param.pg_host = NULL;
|
||||
param.pg_port = 0;
|
||||
param.verbose = 0;
|
||||
param.verbose = 0;
|
||||
param.dry_run = 0;
|
||||
|
||||
while( 1 ) {
|
||||
c = getopt(argc, argv, "?h:U:p:vnW");
|
||||
if(c == -1)
|
||||
break;
|
||||
while (1)
|
||||
{
|
||||
c = getopt(argc, argv, "?h:U:p:vnW");
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch(c) {
|
||||
case '?':
|
||||
if(optopt == '?') {
|
||||
usage();
|
||||
exit(0);
|
||||
}
|
||||
exit(1);
|
||||
case ':':
|
||||
exit(1);
|
||||
case 'v':
|
||||
param.verbose = 1;
|
||||
break;
|
||||
case 'n':
|
||||
param.dry_run = 1;
|
||||
param.verbose = 1;
|
||||
break;
|
||||
case 'U':
|
||||
param.pg_user = strdup(optarg);
|
||||
break;
|
||||
case 'W':
|
||||
param.pg_prompt = 1;
|
||||
break;
|
||||
case 'p':
|
||||
port = strtol(optarg, NULL, 10);
|
||||
if( (port < 1) || (port > 65535)) {
|
||||
fprintf(stderr, "[%s]: invalid port number '%s'\n", argv[0], optarg);
|
||||
exit(1);
|
||||
}
|
||||
param.pg_port = strdup(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
param.pg_host = strdup(optarg);
|
||||
break;
|
||||
}
|
||||
switch (c)
|
||||
{
|
||||
case '?':
|
||||
if (optopt == '?')
|
||||
{
|
||||
usage();
|
||||
exit(0);
|
||||
}
|
||||
exit(1);
|
||||
case ':':
|
||||
exit(1);
|
||||
case 'v':
|
||||
param.verbose = 1;
|
||||
break;
|
||||
case 'n':
|
||||
param.dry_run = 1;
|
||||
param.verbose = 1;
|
||||
break;
|
||||
case 'U':
|
||||
param.pg_user = strdup(optarg);
|
||||
break;
|
||||
case 'W':
|
||||
param.pg_prompt = 1;
|
||||
break;
|
||||
case 'p':
|
||||
port = strtol(optarg, NULL, 10);
|
||||
if ((port < 1) || (port > 65535))
|
||||
{
|
||||
fprintf(stderr, "[%s]: invalid port number '%s'\n", argv[0], optarg);
|
||||
exit(1);
|
||||
}
|
||||
param.pg_port = strdup(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
param.pg_host = strdup(optarg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* No database given? Show usage */
|
||||
if(optind >= argc-1) {
|
||||
fprintf(stderr, "vacuumlo: missing required argument: database name\n");
|
||||
fprintf(stderr, "Try 'vacuumlo -?' for help.\n");
|
||||
exit(1);
|
||||
if (optind >= argc - 1)
|
||||
{
|
||||
fprintf(stderr, "vacuumlo: missing required argument: database name\n");
|
||||
fprintf(stderr, "Try 'vacuumlo -?' for help.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for(c = optind; c < argc; c++) {
|
||||
/* Work on selected database */
|
||||
rc += (vacuumlo(argv[c], ¶m) != 0);
|
||||
for (c = optind; c < argc; c++)
|
||||
{
|
||||
/* Work on selected database */
|
||||
rc += (vacuumlo(argv[c], ¶m) != 0);
|
||||
}
|
||||
|
||||
return rc;
|
||||
|
Reference in New Issue
Block a user