CUBRID Engine  latest
db_value_printer.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2008 Search Solution Corporation
3  * Copyright 2016 CUBRID Corporation
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 /*
20  * db_value_printer.cpp
21  */
22 
23 #include "db_value_printer.hpp"
24 
25 #include "db_date.h"
26 #include "dbtype.h"
28 #include "object_primitive.h"
29 #include "object_representation.h"
30 #include "printer.hpp"
31 #include "set_object.h"
32 #include "string_buffer.hpp"
33 #include "string_opfunc.h"
34 #include "tz_support.h"
35 #if !defined(SERVER_MODE)
36 #include "virtual_object.h"
37 #endif
38 
39 const char db_value_printer::DECIMAL_FORMAT[] = "%#.*g";
40 
41 namespace
42 {
43  //--------------------------------------------------------------------------------
44  // DB_VALUE of type DB_TYPE_BIT or DB_TYPE_VARBIT
45  void describe_bit_string (string_buffer &buf, const db_value *value, bool pad_byte)
46  {
47  const unsigned char *bstring;
48  int nibble_length, nibbles, count;
49 
50  assert (value != NULL);
51 
52  bstring = REINTERPRET_CAST (const unsigned char *, db_get_string (value));
53  if (bstring == NULL)
54  {
55  return;
56  }
57 
58  nibble_length = ((db_get_string_length (value) + 3) / 4);
59  for (nibbles = 0, count = 0; nibbles < nibble_length - 1; count++, nibbles += 2)
60  {
61  buf ("%02x", bstring[count]);
62  }
63 
64  /* If we don't have a full byte on the end, print the nibble. */
65  if (nibbles < nibble_length)
66  {
67  if (pad_byte)
68  {
69  buf ("%02x", bstring[count]);
70  }
71  else
72  {
73  //use only the 1st hex digit
74  char tmp[3] = {0};
75  sprintf (tmp, "%1x", bstring[count]);
76  buf += tmp[0];
77  }
78  }
79  }
80 
81  //--------------------------------------------------------------------------------
82  void describe_real (string_buffer &buf, double value, int precision)
83  {
84  char tbuf[24];
85 
86  snprintf (tbuf, sizeof (tbuf), db_value_printer::DECIMAL_FORMAT, precision, value);
87  if (strstr (tbuf, "Inf"))
88  {
89  snprintf (tbuf, sizeof (tbuf), db_value_printer::DECIMAL_FORMAT, precision, (value > 0 ? FLT_MAX : -FLT_MAX));
90  }
91  buf (tbuf);
92  }
93 }//namespace
94 
95 //--------------------------------------------------------------------------------
97 {
98  assert (value != NULL);
99 
100  m_buf ("%s%.2f", intl_get_money_esc_ISO_symbol (value->type), value->amount);
101  if (strstr (m_buf.get_buffer (), "Inf"))
102  {
103  m_buf ("%s%.2f", intl_get_money_esc_ISO_symbol (value->type), (value->amount > 0 ? DBL_MAX : -DBL_MAX));
104  }
105 }
106 
107 //--------------------------------------------------------------------------------
109 {
111 
112  if (DB_IS_NULL (value))
113  {
114  m_buf ("NULL");
115  }
116  else
117  {
118  /* add some extra info to the basic data value */
119  switch (DB_VALUE_TYPE (value))
120  {
121  case DB_TYPE_CHAR:
122  case DB_TYPE_VARCHAR:
123  codeset = db_get_string_codeset (value);
124  if (codeset != LANG_SYS_CODESET)
125  {
126  m_buf ("%s", lang_charset_introducer (codeset));
127  }
128  m_buf += '\'';
129  describe_data (value);
130  m_buf += '\'';
131  break;
132 
133  case DB_TYPE_ENUMERATION:
134  if (db_get_enum_string (value) == NULL && db_get_enum_short (value) != 0)
135  {
136  /* to print enum index as int */
137  m_buf ("%d", (int)db_get_enum_short (value));
138  break;
139  }
140  else
141  {
142  DB_VALUE varchar_val;
143 
144  /* print enumerations as strings */
145  if (tp_enumeration_to_varchar (value, &varchar_val) == NO_ERROR)
146  {
147  codeset = (INTL_CODESET) db_get_enum_codeset (value);
148  if (codeset != LANG_SYS_CODESET)
149  {
150  m_buf ("%s", lang_charset_introducer (codeset));
151  }
152  describe_value (&varchar_val);
153  }
154  else
155  {
156  /* tp_enumeration_to_varchar only fails if the enum string is null which we already checked */
157  assert (false);
158  }
159  }
160  break;
161  case DB_TYPE_DATE:
162  m_buf ("date '");
163  describe_data (value);
164  m_buf += '\'';
165  break;
166 
167  case DB_TYPE_JSON:
168  m_buf ("json '");
169  describe_data (value);
170  m_buf += '\'';
171  break;
172 
173  case DB_TYPE_TIME:
174  m_buf ("time '");
175  describe_data (value);
176  m_buf += '\'';
177  break;
178  case DB_TYPE_TIMESTAMP:
179  m_buf ("timestamp '");
180  describe_data (value);
181  m_buf += '\'';
182  break;
183  case DB_TYPE_TIMESTAMPTZ:
184  m_buf ("timestamptz '");
185  describe_data (value);
186  m_buf += '\'';
187  break;
189  m_buf ("timestampltz '");
190  describe_data (value);
191  m_buf += '\'';
192  break;
193  case DB_TYPE_DATETIME:
194  m_buf ("datetime '");
195  describe_data (value);
196  m_buf += '\'';
197  break;
198  case DB_TYPE_DATETIMETZ:
199  m_buf ("datetimetz '");
200  describe_data (value);
201  m_buf += '\'';
202  break;
203  case DB_TYPE_DATETIMELTZ:
204  m_buf ("datetimeltz '");
205  describe_data (value);
206  m_buf += '\'';
207  break;
208  case DB_TYPE_NCHAR:
209  case DB_TYPE_VARNCHAR:
210  m_buf ("N'");
211  describe_data (value);
212  m_buf += '\'';
213  break;
214  case DB_TYPE_BIT:
215  case DB_TYPE_VARBIT:
216  m_buf ("X'");
217  describe_data (value);
218  m_buf += '\'';
219  break;
220  case DB_TYPE_BLOB:
221  m_buf ("BLOB'");
222  describe_data (value);
223  m_buf += '\'';
224  break;
225  case DB_TYPE_CLOB:
226  m_buf ("CLOB'");
227  describe_data (value);
228  m_buf += '\'';
229  break;
230  default:
231  describe_data (value);
232  break;
233  }
234  }
235 }
236 
237 //--------------------------------------------------------------------------------
239 {
240  OID *oid = 0;
241  db_object *obj = 0;
242  db_monetary *money = 0;
243  DB_SET *set = 0;
244  db_elo *elo = 0;
245  DB_MIDXKEY *midxkey;
246  const char *src, *pos, *end;
247  double d;
248  char line[1025];
249  char *json_body = NULL;
250 
251  if (DB_IS_NULL (value))
252  {
253  m_buf ("NULL");
254  }
255  switch (DB_VALUE_TYPE (value))
256  {
257  case DB_TYPE_INTEGER:
258  m_buf ("%d", db_get_int (value));
259  break;
260 
261  case DB_TYPE_BIGINT:
262  m_buf ("%lld", (long long) db_get_bigint (value));
263  break;
264 
265  case DB_TYPE_POINTER:
266  m_buf ("%p", db_get_pointer (value));
267  break;
268 
269  case DB_TYPE_SHORT:
270  m_buf ("%d", (int) db_get_short (value));
271  break;
272 
273  case DB_TYPE_ERROR:
274  m_buf ("%d", (int) db_get_error (value));
275  break;
276 
277  case DB_TYPE_FLOAT:
278  describe_real (m_buf, db_get_float (value), DB_FLOAT_DECIMAL_PRECISION);
279  break;
280 
281  case DB_TYPE_DOUBLE:
282  describe_real (m_buf, db_get_double (value), DB_DOUBLE_DECIMAL_PRECISION);
283  break;
284 
285  case DB_TYPE_NUMERIC:
286  m_buf ("%s", numeric_db_value_print (value, line));
287  break;
288 
289  case DB_TYPE_BIT:
290  case DB_TYPE_VARBIT:
291  describe_bit_string (m_buf, value, m_padding);
292  break;
293 
294  case DB_TYPE_CHAR:
295  case DB_TYPE_NCHAR:
296  case DB_TYPE_VARCHAR:
297  case DB_TYPE_VARNCHAR:
298  /* Copy string into buf providing for any embedded quotes. Strings may have embedded NULL characters and
299  * embedded quotes. None of the supported multibyte character codesets have a conflict between a quote
300  * character and the second byte of the multibyte character.
301  */
302  src = db_get_string (value);
303  end = src + db_get_string_size (value);
304  while (src < end)
305  {
306  /* Find the position of the next quote or the end of the string, whichever comes first. This loop is
307  * done in place of strchr in case the string has an embedded NULL.
308  */
309  for (pos = src; pos && pos < end && (*pos) != '\''; pos++)
310  ;
311 
312  /* If pos < end, then a quote was found. If so, copy the partial buffer and duplicate the quote */
313  if (pos < end)
314  {
315  m_buf.add_bytes (pos - src + 1, src);
316  m_buf += '\'';
317  }
318  /* If not, copy the remaining part of the buffer */
319  else
320  {
321  m_buf.add_bytes (end - src, src);
322  }
323 
324  /* advance src to just beyond the point where we left off */
325  src = pos + 1;
326  }
327  break;
328 
329  case DB_TYPE_OBJECT:
330 #if defined(SERVER_MODE)
331  assert (false);
332 #else //#if defined(SERVER_MODE)
333  obj = db_get_object (value);
334  if (obj == NULL)
335  {
336  break;
337  }
338  if (obj->is_vid)
339  {
340  DB_VALUE vobj;
341  vid_object_to_vobj (obj, &vobj);
342  describe_value (&vobj);
343  break;
344  }
345  oid = WS_OID (obj);
346  m_buf ("%d|%d|%d", int (oid->volid), int (oid->pageid), int (oid->slotid));
347 #endif //#if defined(SERVER_MODE)
348  break;
349  /* If we are on the server, fall thru to the oid case The value is probably nonsense, but that is safe to do.
350  * This case should simply not occur.
351  */
352 
353  case DB_TYPE_OID:
354  oid = (OID *) db_get_oid (value);
355  if (oid == NULL)
356  {
357  break;
358  }
359  m_buf ("%d|%d|%d", int (oid->volid), int (oid->pageid), int (oid->slotid));
360  break;
361 
362  case DB_TYPE_VOBJ:
363  m_buf ("vid:");
364  /* FALLTHRU */
365  case DB_TYPE_SET:
366  case DB_TYPE_MULTISET:
367  case DB_TYPE_SEQUENCE:
368  set = db_get_set (value);
369  if (set != NULL)
370  {
371  describe_set (set);
372  }
373  else
374  {
375  m_buf ("NULL");
376  }
377  break;
378 
379  case DB_TYPE_JSON:
380  json_body = db_get_json_raw_body (value);
381  m_buf ("%s", json_body);
382  db_private_free (NULL, json_body);
383  break;
384 
385  case DB_TYPE_MIDXKEY:
386  midxkey = db_get_midxkey (value);
387  if (midxkey != NULL)
388  {
389  describe_midxkey (midxkey);
390  }
391  else
392  {
393  m_buf ("NULL");
394  }
395  break;
396 
397  case DB_TYPE_BLOB:
398  case DB_TYPE_CLOB:
399  elo = db_get_elo (value);
400  if (elo != NULL)
401  {
402  if (elo->type == ELO_FBO)
403  {
404  assert (elo->locator != NULL);
405  m_buf ("%s", elo->locator);
406  }
407  else /* ELO_LO */
408  {
409  /* should not happen for now */
410  assert (0);
411  }
412  }
413  else
414  {
415  m_buf ("NULL");
416  }
417  break;
418 
419  /*
420  * This constant is necessary to fake out the db_?_to_string()
421  * routines that are expecting a buffer length. Since we assume
422  * that our buffer is big enough in this code, just pass something
423  * that ought to work for every case.
424  */
425 #define TOO_BIG_TO_MATTER 1024
426 
427  case DB_TYPE_TIME:
428  (void) db_time_to_string (line, TOO_BIG_TO_MATTER, db_get_time (value));
429  m_buf (line);
430  break;
431 
432  case DB_TYPE_TIMESTAMP:
433  (void) db_utime_to_string (line, TOO_BIG_TO_MATTER, db_get_timestamp (value));
434  m_buf (line);
435  break;
436 
439  m_buf (line);
440  break;
441 
442  case DB_TYPE_TIMESTAMPTZ:
443  {
444  DB_TIMESTAMPTZ *ts_tz;
445 
446  ts_tz = db_get_timestamptz (value);
447  (void) db_timestamptz_to_string (line, TOO_BIG_TO_MATTER, & (ts_tz->timestamp), & (ts_tz->tz_id));
448  m_buf (line);
449  }
450  break;
451 
452  case DB_TYPE_DATETIME:
454  m_buf (line);
455  break;
456  case DB_TYPE_DATETIMELTZ:
458  m_buf (line);
459  break;
460 
461  case DB_TYPE_DATETIMETZ:
462  {
463  DB_DATETIMETZ *dt_tz;
464 
465  dt_tz = db_get_datetimetz (value);
466  (void) db_datetimetz_to_string (line, TOO_BIG_TO_MATTER, & (dt_tz->datetime), & (dt_tz->tz_id));
467  m_buf (line);
468  }
469  break;
470 
471  case DB_TYPE_DATE:
472  (void) db_date_to_string (line, TOO_BIG_TO_MATTER, db_get_date (value));
473  m_buf (line);
474  break;
475 
476  case DB_TYPE_MONETARY:
477  money = db_get_monetary (value);
478  OR_MOVE_DOUBLE (&money->amount, &d);
479  describe_money (money);
480  break;
481 
482  case DB_TYPE_NULL:
483  /* Can't get here because the DB_IS_NULL test covers DB_TYPE_NULL */
484  break;
485 
486  case DB_TYPE_VARIABLE:
487  case DB_TYPE_SUB:
488  case DB_TYPE_DB_VALUE:
489  /* make sure line is NULL terminated, may not be necessary line[0] = '\0'; */
490  break;
491 
492  default:
493  /* NB: THERE MUST BE NO DEFAULT CASE HERE. ALL TYPES MUST BE HANDLED! */
494  assert (false);
495  break;
496  }
497 }
498 
499 //--------------------------------------------------------------------------------
500 void db_value_printer::describe_midxkey (const db_midxkey *midxkey, int help_Max_set_elements)
501 {
502  db_value value;
503  int size, end, i;
504  int prev_i_index;
505  char *prev_i_ptr;
506 
507  assert (midxkey != NULL);
508 
509  m_buf += '{';
510  size = midxkey->ncolumns;
511  if (help_Max_set_elements == 0 || help_Max_set_elements > size)
512  {
513  end = size;
514  }
515  else
516  {
517  end = help_Max_set_elements;
518  }
519 
520  prev_i_index = 0;
521  prev_i_ptr = NULL;
522  for (i = 0; i < end; i++)
523  {
524  pr_midxkey_get_element_nocopy (midxkey, i, &value, &prev_i_index, &prev_i_ptr);
525  describe_value (&value);
526  if (i < size - 1)
527  {
528  m_buf (", ");
529  }
530  if (!DB_IS_NULL (&value) && value.need_clear == true)
531  {
532  pr_clear_value (&value);
533  }
534  }
535  if (i < size)
536  {
537  m_buf (". . .");
538  }
539  m_buf += '}';
540 }
541 
542 //--------------------------------------------------------------------------------
543 void db_value_printer::describe_set (const db_set *set, int help_Max_set_elements)
544 {
545  DB_VALUE value;
546  int size, end, i;
547 
548  assert (set != NULL);
549 
550  m_buf += '{';
551  size = set_size ((DB_COLLECTION *)set);
552  if (help_Max_set_elements == 0 || help_Max_set_elements > size)
553  {
554  end = size;
555  }
556  else
557  {
558  end = help_Max_set_elements;
559  }
560 
561  for (i = 0; i < end; ++i)
562  {
563  set_get_element ((DB_COLLECTION *)set, i, &value);
564  describe_value (&value);
565  db_value_clear (&value);
566  if (i < size - 1)
567  {
568  m_buf (", ");
569  }
570  }
571  if (i < size)
572  {
573  m_buf (". . .");
574  }
575  m_buf += '}';
576 }
577 
578 /*
579  * db_value_fprint() - Prints a description of the contents of a DB_VALUE
580  * to the file
581  * return: none
582  * fp(in) : FILE stream pointer
583  * value(in) : value to print
584  */
585 void
586 db_fprint_value (FILE *fp, const db_value *value)
587 {
588  const size_t BUFFER_SIZE = 1024;
590 
591  db_value_printer printer (sb);
592  printer.describe_value (value);
593  fprintf (fp, "%.*s", (int) sb.len (), sb.get_buffer ());
594 }
595 
596 /*
597  * db_print_value() - Prints a description of the contents of a DB_VALUE
598  * to the file
599  * return: none
600  * fp(in) : FILE stream pointer
601  * value(in) : value to print
602  */
603 void
604 db_print_value (print_output &output_ctx, const db_value *value)
605 {
606  string_buffer *p_sb;
607 
608  /* TODO : change 'db_value_printer' to use print_output instead of string_buffer */
609  p_sb = output_ctx.grab_string_buffer ();
610 
611  if (p_sb != NULL)
612  {
613  db_value_printer printer (*p_sb);
614  printer.describe_value (value);
615  }
616  else
617  {
618  const size_t BUFFER_SIZE = 1024;
620 
621  db_value_printer printer (sb);
622  printer.describe_value (value);
623  output_ctx ("%.*s", (int) sb.len (), sb.get_buffer ());
624  }
625 }
626 
627 /*
628  * db_sprint_value() - This places a printed representation of the supplied value in a buffer.
629  * value(in) : value to describe
630  * sb(in/out) : auto resizable buffer to contain description
631  */
632 void
634 {
635  db_value_printer printer (sb);
636  printer.describe_value (value);
637 }
DB_C_FLOAT db_get_float(const DB_VALUE *value)
#define NO_ERROR
Definition: error_code.h:46
DB_COLLECTION * db_get_set(const DB_VALUE *value)
DB_MIDXKEY * db_get_midxkey(const DB_VALUE *value)
int db_timestamptz_to_string(char *buf, int bufsize, DB_TIMESTAMP *utime, const TZ_ID *tz_id)
Definition: db_date.c:4090
int db_date_to_string(char *buf, int bufsize, DB_DATE *date)
Definition: db_date.c:3953
int db_get_int(const DB_VALUE *value)
DB_TIMESTAMP timestamp
Definition: dbtype_def.h:766
string_buffer & m_buf
DB_C_DOUBLE db_get_double(const DB_VALUE *value)
DB_ELO_TYPE type
Definition: dbtype_def.h:950
int db_get_enum_codeset(const DB_VALUE *value)
char * db_get_json_raw_body(const DB_VALUE *value)
Definition: db_macro.c:5082
int db_timestampltz_to_string(char *buf, int bufsize, DB_TIMESTAMP *utime)
Definition: db_date.c:4146
void describe_set(const db_set *set, int help_Max_set_elements=20)
#define BUFFER_SIZE
Definition: broker.c:106
DB_DATETIMETZ * db_get_datetimetz(const DB_VALUE *value)
int vid_object_to_vobj(const DB_OBJECT *obj, DB_VALUE *vobj)
void describe_midxkey(const db_midxkey *midxkey, int help_Max_set_elements=20)
void describe_data(const db_value *value)
const block_allocator PRIVATE_BLOCK_ALLOCATOR
int set_size(DB_COLLECTION *set)
Definition: set_object.c:3036
DB_ELO * db_get_elo(const DB_VALUE *value)
int db_datetimetz_to_string(char *buf, int bufsize, DB_DATETIME *dt, const TZ_ID *tz_id)
Definition: db_date.c:4302
#define DB_FLOAT_DECIMAL_PRECISION
Definition: dbtype_def.h:589
void describe_value(const db_value *value)
#define REINTERPRET_CAST(dest_type, expr)
Definition: porting.h:1080
DB_TIMESTAMPTZ * db_get_timestamptz(const DB_VALUE *value)
int size
Definition: set_object.h:72
DB_MONETARY * db_get_monetary(const DB_VALUE *value)
Definition: db_set.h:35
#define assert(x)
#define DB_DOUBLE_DECIMAL_PRECISION
Definition: dbtype_def.h:592
string_buffer * grab_string_buffer(void)
Definition: printer.hpp:49
void db_print_value(print_output &output_ctx, const db_value *value)
DB_DATETIME datetime
Definition: dbtype_def.h:783
DB_OBJECT * db_get_object(const DB_VALUE *value)
void describe_money(const db_monetary *value)
#define NULL
Definition: freelistheap.h:34
unsigned short db_get_enum_short(const DB_VALUE *value)
DB_CURRENCY type
Definition: dbtype_def.h:832
#define TOO_BIG_TO_MATTER
const char * get_buffer() const
#define db_private_free(thrd, ptr)
Definition: memory_alloc.h:229
int set_get_element(DB_COLLECTION *set, int index, DB_VALUE *value)
Definition: set_object.c:2575
int pr_midxkey_get_element_nocopy(const DB_MIDXKEY *midxkey, int index, DB_VALUE *value, int *prev_indexp, char **prev_ptrp)
need_clear_type need_clear
Definition: dbtype_def.h:1084
int count(int &result, const cub_regex_object &reg, const std::string &src, const int position, const INTL_CODESET codeset)
#define db_utime_to_string
Definition: tz_support.h:29
int pr_clear_value(DB_VALUE *value)
DB_BIGINT db_get_bigint(const DB_VALUE *value)
int db_time_to_string(char *buf, int bufsize, DB_TIME *time)
Definition: db_date.c:3994
int db_datetime_to_string(char *buf, int bufsize, DB_DATETIME *datetime)
Definition: db_date.c:4225
size_t len() const
int ncolumns
Definition: dbtype_def.h:864
DB_CONST_C_CHAR db_get_enum_string(const DB_VALUE *value)
char * numeric_db_value_print(const DB_VALUE *val, char *buf)
void db_sprint_value(const db_value *value, string_buffer &sb)
OID * db_get_oid(const DB_VALUE *value)
#define WS_OID(mop)
Definition: work_space.h:293
void db_fprint_value(FILE *fp, const db_value *value)
char * locator
Definition: dbtype_def.h:948
DB_DATE * db_get_date(const DB_VALUE *value)
#define OR_MOVE_DOUBLE(src, dst)
Definition: byte_order.h:89
enum intl_codeset INTL_CODESET
Definition: intl_support.h:190
static const char DECIMAL_FORMAT[]
DB_TIMESTAMP * db_get_timestamp(const DB_VALUE *value)
int db_get_string_size(const DB_VALUE *value)
DB_C_SHORT db_get_short(const DB_VALUE *value)
int tp_enumeration_to_varchar(const DB_VALUE *src, DB_VALUE *result)
#define DB_VALUE_TYPE(value)
Definition: dbtype.h:72
int i
Definition: dynamic_load.c:954
#define DB_IS_NULL(value)
Definition: dbtype.h:63
int db_get_error(const DB_VALUE *value)
DB_DATETIME * db_get_datetime(const DB_VALUE *value)
const char * lang_charset_introducer(const INTL_CODESET codeset)
int db_value_clear(DB_VALUE *value)
Definition: db_macro.c:1588
int db_get_string_length(const DB_VALUE *value)
#define LANG_SYS_CODESET
DB_TIME * db_get_time(const DB_VALUE *value)
DB_C_POINTER db_get_pointer(const DB_VALUE *value)
double amount
Definition: dbtype_def.h:831
int db_get_string_codeset(const DB_VALUE *value)
void add_bytes(size_t len, const char *bytes)
DB_CONST_C_CHAR db_get_string(const DB_VALUE *value)
int db_datetimeltz_to_string(char *buf, int bufsize, DB_DATETIME *dt)
Definition: db_date.c:4350
char * intl_get_money_esc_ISO_symbol(const DB_CURRENCY currency)
unsigned is_vid
Definition: work_space.h:148