/*
* %CopyrightBegin%
*
* Copyright Ericsson AB 1998-2009. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* %CopyrightEnd%
*/
#ifndef _DB_UTIL_H
#define _DB_UTIL_H
#include "global.h"
#include "erl_message.h"
/*#define HARDDEBUG 1*/
#ifdef DEBUG
/*
** DMC_DEBUG does NOT need DEBUG, but DEBUG needs DMC_DEBUG
*/
#define DMC_DEBUG 1
#endif
/*
* These values can be returned from the functions performing the
* BIF operation for different types of tables. When the
* actual operations have been performed, the BIF function
* checks for negative returns and issues BIF_ERRORS based
* upon these values.
*/
#define DB_ERROR_NONE 0 /* No error */
#define DB_ERROR_BADITEM -1 /* The item was malformed ie no
tuple or to small*/
#define DB_ERROR_BADTABLE -2 /* The Table is inconsisitent */
#define DB_ERROR_SYSRES -3 /* Out of system resources */
#define DB_ERROR_BADKEY -4 /* Returned if a key that should
exist does not. */
#define DB_ERROR_BADPARAM -5 /* Returned if a specified slot does
not exist (hash table only) or
the state parameter in db_match_object
is broken.*/
#define DB_ERROR_UNSPEC -10 /* Unspecified error */
/*
* A datatype for a database entry stored out of a process heap
*/
typedef struct db_term {
ErlOffHeap off_heap; /* Off heap data for term. */
Uint size; /* Size of term in "words" */
Eterm tpl[1]; /* Untagged "constant pointer" to top tuple */
/* (assumed to be first in buffer) */
} DbTerm;
/* "Assign" a value to DbTerm.tpl */
#define DBTERM_SET_TPL(dbtermPtr,tplPtr) ASSERT((tplPtr)==(dbtermPtr->tpl))
/* Get start of term buffer */
#define DBTERM_BUF(dbtermPtr) ((dbtermPtr)->tpl)
union db_table;
typedef union db_table DbTable;
/* Info about a database entry while it's being updated
* (by update_counter or update_element)
*/
typedef struct {
DbTable* tb;
DbTerm* dbterm;
void** bp; /* {Hash|Tree}DbTerm** */
Uint new_size;
int mustResize;
void* lck;
} DbUpdateHandle;
typedef struct db_table_method
{
int (*db_create)(Process *p, DbTable* tb);
int (*db_first)(Process* p,
DbTable* tb, /* [in out] */
Eterm* ret /* [out] */);
int (*db_next)(Process* p,
DbTable* tb, /* [in out] */
Eterm key, /* [in] */
Eterm* ret /* [out] */);
int (*db_last)(Process* p,
DbTable* tb, /* [in out] */
Eterm* ret /* [out] */);
int (*db_prev)(Process* p,
DbTable* tb, /* [in out] */
Eterm key,
Eterm* ret);
int (*db_put)(DbTable* tb, /* [in out] */
Eterm obj,
int key_clash_fail); /* DB_ERROR_BADKEY if key exists */
int (*db_get)(Process* p,
DbTable* tb, /* [in out] */
Eterm key,
Eterm* ret);
int (*db_get_element)(Process* p,
DbTable* tb, /* [in out] */
Eterm key,
int index,
Eterm* ret);
int (*db_member)(DbTable* tb, /* [in out] */
Eterm key,
Eterm* ret);
int (*db_erase)(DbTable* tb, /* [in out] */
Eterm key,
Eterm* ret);
int (*db_erase_object)(DbTable* tb, /* [in out] */
Eterm obj,
Eterm* ret);
int (*db_slot)(Process* p,
DbTable* tb, /* [in out] */
Eterm slot,
Eterm* ret);
int (*db_select_chunk)(Process* p,
DbTable* tb, /* [in out] */
Eterm pattern,
Sint chunk_size,
int reverse,
Eterm* ret);
int (*db_select)(Process* p,
DbTable* tb, /* [in out] */
Eterm pattern,
int reverse,
Eterm* ret);
int (*db_select_delete)(Process* p,
DbTable* tb, /* [in out] */
Eterm pattern,
Eterm* ret);
int (*db_select_continue)(Process* p,
DbTable* tb, /* [in out] */
Eterm continuation,
Eterm* ret);
int (*db_select_delete_continue)(Process* p,
DbTable* tb, /* [in out] */
Eterm continuation,
Eterm* ret);
int (*db_select_count)(Process* p,
DbTable* tb, /* [in out] */
Eterm pattern,
Eterm* ret);
int (*db_select_count_continue)(Process* p,
DbTable* tb, /* [in out] */
Eterm continuation,
Eterm* ret);
int (*db_delete_all_objects)(Process* p,
DbTable* db /* [in out] */ );
int (*db_free_table)(DbTable* db /* [in out] */ );
int (*db_free_table_continue)(DbTable* db); /* [in out] */
void (*db_print)(int to,
void* to_arg,
int show,
DbTable* tb /* [in out] */ );
void (*db_foreach_offheap)(DbTable* db, /* [in out] */
void (*func)(ErlOffHeap *, void *),
void *arg);
void (*db_check_table)(DbTable* tb);
/* Lookup a dbterm for updating. Return false if not found.
*/
int (*db_lookup_dbterm)(DbTable*, Eterm key,
DbUpdateHandle* handle); /* [out] */
/* Must be called for each db_lookup_dbterm that returned true,
** even if dbterm was not updated.
*/
void (*db_finalize_dbterm)(DbUpdateHandle* handle);
} DbTableMethod;
/*
* This structure contains data for all different types of database
* tables. Note that these fields must match the same fields
* in the table-type specific structures.
* The reason it is placed here and not in db.h is that some table
* operations may be the same on different types of tables.
*/
typedef struct db_fixation {
Eterm pid;
Uint counter;
struct db_fixation *next;
} DbFixation;
typedef struct db_table_common {
erts_refc_t ref;
erts_refc_t fixref; /* fixation counter */
#ifdef ERTS_SMP
erts_smp_rwmtx_t rwlock; /* rw lock on table */
erts_smp_mtx_t fixlock; /* Protects fixations,megasec,sec,microsec */
int is_thread_safe; /* No fine locking inside table needed */
Uint32 type; /* table type, *read only* after creation */
#endif
Eterm owner; /* Pid of the creator */
Eterm heir; /* Pid of the heir */
UWord heir_data; /* To send in ETS-TRANSFER (is_immed or (DbTerm*) */
SysTimeval heir_started; /* To further identify the heir */
Eterm the_name; /* an atom */
Eterm id; /* atom | integer */
DbTableMethod* meth; /* table methods */
erts_smp_atomic_t nitems; /* Total number of items in table */
erts_smp_atomic_t memory_size;/* Total memory size. NOTE: in bytes! */
Uint megasec,sec,microsec; /* Last fixation time */
DbFixation* fixations; /* List of processes who have done safe_fixtable,
"local" fixations not included. */
/* All 32-bit fields */
Uint32 status; /* bit masks defined below */
int slot; /* slot index in meta_main_tab */
int keypos; /* defaults to 1 */
} DbTableCommon;
/* These are status bit patterns */
#define DB_NORMAL (1 << 0)
#define DB_PRIVATE (1 << 1)
#define DB_PROTECTED (1 << 2)
#define DB_PUBLIC (1 << 3)
#define DB_BAG (1 << 4)
#define DB_SET (1 << 5)
/*#define DB_LHASH (1 << 6)*/
#define DB_FINE_LOCKED (1 << 7) /* fine grained locking enabled */
#define DB_DUPLICATE_BAG (1 << 8)
#define DB_ORDERED_SET (1 << 9)
#define DB_DELETE (1 << 10) /* table is being deleted */
#define ERTS_ETS_TABLE_TYPES (DB_BAG|DB_SET|DB_DUPLICATE_BAG|DB_ORDERED_SET|DB_FINE_LOCKED)
#define IS_HASH_TABLE(Status) (!!((Status) & \
(DB_BAG | DB_SET | DB_DUPLICATE_BAG)))
#define IS_TREE_TABLE(Status) (!!((Status) & \
DB_ORDERED_SET))
#define NFIXED(T) (erts_refc_read(&(T)->common.fixref,0))
#define IS_FIXED(T) (NFIXED(T) != 0)
Eterm erts_ets_copy_object(Eterm, Process*);
/* optimised version of copy_object (normal case? atomic object) */
#define COPY_OBJECT(obj, p, objp) \
if (IS_CONST(obj)) { *(objp) = (obj); } \
else { *objp = erts_ets_copy_object(obj, p); }
#define DB_READ (DB_PROTECTED|DB_PUBLIC)
#define DB_WRITE DB_PUBLIC
#define DB_INFO (DB_PROTECTED|DB_PUBLIC|DB_PRIVATE)
/* tb is an DbTableCommon and obj is an Eterm (tagged) */
#define TERM_GETKEY(tb, obj) db_getkey((tb)->common.keypos, (obj))
#define ONLY_WRITER(P,T) (((T)->common.status & (DB_PRIVATE|DB_PROTECTED)) \
&& (T)->common.owner == (P)->id)
#define ONLY_READER(P,T) (((T)->common.status & DB_PRIVATE) && \
(T)->common.owner == (P)->id)
/* Function prototypes */
Eterm db_get_trace_control_word_0(Process *p);
Eterm db_set_trace_control_word_1(Process *p, Eterm val);
void db_initialize_util(void);
Eterm db_getkey(int keypos, Eterm obj);
void db_free_term_data(DbTerm* p);
void* db_get_term(DbTableCommon *tb, DbTerm* old, Uint offset, Eterm obj);
int db_has_variable(Eterm obj);
int db_is_variable(Eterm obj);
void db_do_update_element(DbUpdateHandle* handle,
Sint position,
Eterm newval);
void db_finalize_update_element(DbUpdateHandle* handle);
Eterm db_add_counter(Eterm** hpp, Eterm counter, Eterm incr);
Eterm db_match_set_lint(Process *p, Eterm matchexpr, Uint flags);
Binary *db_match_set_compile(Process *p, Eterm matchexpr,
Uint flags);
void erts_db_match_prog_destructor(Binary *);
typedef struct match_prog {
ErlHeapFragment *term_save; /* Only if needed, a list of message
buffers for off heap copies
(i.e. binaries)*/
int single_variable; /* ets:match needs to know this. */
int num_bindings; /* Size of heap */
/* The following two are only filled in when match specs
are used for tracing */
struct erl_heap_fragment *saved_program_buf;
Eterm saved_program;
Uint heap_size; /* size of: heap + eheap + stack */
Uint eheap_offset;
Uint stack_offset;
#ifdef DMC_DEBUG
UWord* prog_end; /* End of program */
#endif
UWord text[1]; /* Beginning of program */
} MatchProg;
/*
* The heap-eheap-stack block of a MatchProg is nowadays allocated
* when the match program is run.
* - heap: variable bindings
* - eheap: erlang heap storage
* - eheap: a "large enough" stack
*/
#define DMC_ERR_STR_LEN 100
typedef enum { dmcWarning, dmcError} DMCErrorSeverity;
typedef struct dmc_error {
char error_string[DMC_ERR_STR_LEN + 1]; /* printf format string
with %d for the variable
number (if applicable) */
int variable; /* -1 if no variable is referenced
in error string */
struct dmc_error *next;
DMCErrorSeverity severity; /* Error or warning */
} DMCError;
typedef struct dmc_err_info {
unsigned int *var_trans; /* Translations of variable names,
initiated to NULL
and free'd with sys_free if != NULL
after compilation */
int num_trans;
int error_added; /* indicates if the error list contains
any fatal errors (dmcError severity) */
DMCError *first; /* List of errors */
} DMCErrInfo;
/*
** Compilation flags
**
** The dialect is in the 3 least significant bits and are to be interspaced by
** by at least 2 (decimal), thats why ((Uint) 2) isn't used. This is to be
** able to add DBIF_GUARD or DBIF BODY to it to use in the match_spec bif
** table. The rest of the word is used like ordinary flags, one bit for each
** flag. Note that DCOMP_TABLE and DCOMP_TRACE are mutually exclusive.
*/
#define DCOMP_TABLE ((Uint) 1) /* Ets and dets. The body returns a value,
* and the parameter to the execution is a tuple. */
#define DCOMP_TRACE ((Uint) 4) /* Trace. More functions are allowed, and the
* parameter to the execution will be an array. */
#define DCOMP_DIALECT_MASK ((Uint) 0x7) /* To mask out the bits marking
dialect */
#define DCOMP_FAKE_DESTRUCTIVE ((Uint) 8) /* When this is active, no setting of
trace control words or seq_trace tokens will be done. */
Binary *db_match_compile(Eterm *matchexpr, Eterm *guards,
Eterm *body, int num_matches,
Uint flags,
DMCErrInfo *err_info);
/* Returns newly allocated MatchProg binary with refc == 0*/
Eterm db_prog_match(Process *p, Binary *prog, Eterm term, int arity,
Uint32 *return_flags /* Zeroed on enter */);
/* returns DB_ERROR_NONE if matches, 1 if not matches and some db error on
error. */
DMCErrInfo *db_new_dmc_err_info(void);
/* Returns allocated error info, where errors are collected for lint. */
Eterm db_format_dmc_err_info(Process *p, DMCErrInfo *ei);
/* Formats an error info structure into a list of tuples. */
void db_free_dmc_err_info(DMCErrInfo *ei);
/* Completely free's an error info structure, including all recorded
errors */
Eterm db_make_mp_binary(Process *p, Binary *mp, Eterm **hpp);
/* Convert a match program to a erlang "magic" binary to be returned to userspace,
increments the reference counter. */
int erts_db_is_compiled_ms(Eterm term);
/*
** Convenience when compiling into Binary structures
*/
#define IsMatchProgBinary(BP) \
(((BP)->flags & BIN_FLAG_MAGIC) \
&& ERTS_MAGIC_BIN_DESTRUCTOR((BP)) == erts_db_match_prog_destructor)
#define Binary2MatchProg(BP) \
(ASSERT_EXPR(IsMatchProgBinary((BP))), \
((MatchProg *) ERTS_MAGIC_BIN_DATA((BP))))
/*
** Debugging
*/
#ifdef HARDDEBUG
void db_check_tables(void); /* in db.c */
#define CHECK_TABLES() db_check_tables()
#else
#define CHECK_TABLES()
#endif
#endif /* _DB_UTIL_H */