-
Notifications
You must be signed in to change notification settings - Fork 0
/
plmruby_tuple_converter.c
155 lines (127 loc) · 3.56 KB
/
plmruby_tuple_converter.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#include <postgres.h>
#include <access/htup_details.h>
#include <utils/memutils.h>
#include <mruby.h>
#include <mruby/array.h>
#include <mruby/hash.h>
#include <funcapi.h>
#include "plmruby_type.h"
#include "plmruby_tuple_converter.h"
tuple_converter *
new_tuple_converter(mrb_state *mrb, TupleDesc tupdesc)
{
tuple_converter *converter = palloc(sizeof(tuple_converter));
converter->mrb = mrb;
converter->tupdesc = tupdesc;
converter->memcontext = AllocSetContextCreate(
CurrentMemoryContext,
"ConverterContext",
ALLOCSET_SMALL_MINSIZE,
ALLOCSET_SMALL_INITSIZE,
ALLOCSET_SMALL_MAXSIZE);
MemoryContext old_context = MemoryContextSwitchTo(converter->memcontext);
converter->colnames = palloc(sizeof(mrb_value) * tupdesc->natts);
converter->coltypes = palloc(sizeof(plmruby_type) * tupdesc->natts);
MemoryContextSwitchTo(old_context);
for (int i = 0; i < tupdesc->natts; ++i)
{
if (tupdesc->attrs[i]->attisdropped)
continue;
converter->colnames[i] = mrb_symbol_value(mrb_intern_cstr(
mrb, NameStr(tupdesc->attrs[i]->attname)));
plmruby_fill_type(&converter->coltypes[i],
tupdesc->attrs[i]->atttypid,
converter->memcontext);
}
return converter;
}
void
delete_tuple_converter(tuple_converter *converter)
{
if (converter->memcontext != NULL)
{
MemoryContextDelete(converter->memcontext);
converter->memcontext = NULL;
}
pfree(converter);
}
mrb_value
tuple_to_mrb_value(tuple_converter *converter, HeapTuple tuple)
{
mrb_state *mrb = converter->mrb;
int natts = converter->tupdesc->natts;
mrb_value hash = mrb_hash_new_capa(converter->mrb, natts);
for (int i = 0; i < natts; ++i)
{
Datum datum;
bool isnull;
if (converter->tupdesc->attrs[i]->attisdropped)
continue;
datum = heap_getattr(tuple, i + 1, converter->tupdesc, &isnull);
mrb_hash_set(converter->mrb, hash, converter->colnames[i],
datum_to_mrb_value(mrb, datum, isnull, &converter->coltypes[i]));
}
return hash;
}
HeapTuple
mrb_value_to_heap_tuple(tuple_converter *converter, mrb_value value, Tuplestorestate *tupstore, bool is_scalar)
{
HeapTuple result;
mrb_state *mrb = converter->mrb;
TupleDesc tupdesc = converter->tupdesc;
int natts = tupdesc->natts;
// TODO should call BlessTupleDesc(tupdesc) ?
if (!is_scalar && !mrb_hash_p(value))
elog(ERROR, "Only hash can be converted into tuple");
Datum *values = (Datum *) palloc(sizeof(Datum) * natts);
bool *nulls = (bool *) palloc(sizeof(bool) * natts);
if (!is_scalar)
{
mrb_value keys = mrb_hash_keys(mrb, value);
for (int i = 0; i < natts; ++i)
{
if (tupdesc->attrs[i]->attisdropped)
continue;
bool found = false;
mrb_value colname = converter->colnames[i];
for (int j = 0; j < tupdesc->natts; ++j)
{
// TODO ignore an order of hash keys
mrb_value key = mrb_ary_ref(mrb, keys, j);
if (mrb_eql(mrb, colname, key))
{
found = true;
break;
}
}
if (!found)
elog(ERROR, "field name / property name mismatch");
}
}
for (int i = 0; i < natts; ++i)
{
/* Make sure dropped columns are skipped by backend code. */
if (tupdesc->attrs[i]->attisdropped)
{
nulls[i] = true;
continue;
}
mrb_value attr = is_scalar ? value : mrb_hash_get(mrb, value, converter->colnames[i]);
if (mrb_nil_p(attr) || mrb_undef_p(attr))
nulls[i] = true;
else
values[i] = mrb_value_to_datum(mrb, attr, &nulls[i], &converter->coltypes[i]);
}
if (tupstore)
{
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
result = NULL;
}
else
{
result = heap_form_tuple(tupdesc, values, nulls);
}
pfree(values);
pfree(nulls);
return result;
}