construct/matrix/event_fetch.cc

442 lines
7.9 KiB
C++

// Matrix Construct
//
// Copyright (C) Matrix Construct Developers, Authors & Contributors
// Copyright (C) 2016-2018 Jason Volk <jason@zemos.net>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
//
// seek
//
void
ircd::m::seek(event::fetch &fetch,
const event::id &event_id)
{
if(!seek(std::nothrow, fetch, event_id))
throw m::NOT_FOUND
{
"%s not found in database",
event_id
};
}
bool
ircd::m::seek(std::nothrow_t,
event::fetch &fetch,
const event::id &event_id)
{
const auto &event_idx
{
index(std::nothrow, event_id)
};
return seek(std::nothrow, fetch, event_idx, event_id);
}
void
ircd::m::seek(event::fetch &fetch,
const event::idx &event_idx)
{
if(!seek(std::nothrow, fetch, event_idx))
throw m::NOT_FOUND
{
"%lu not found in database",
event_idx
};
}
bool
ircd::m::seek(std::nothrow_t,
event::fetch &fetch,
const event::idx &event_idx)
{
return seek(std::nothrow, fetch, event_idx, m::event::id{});
}
bool
ircd::m::seek(std::nothrow_t,
event::fetch &fetch,
const event::idx &event_idx,
const event::id &event_id)
{
fetch.event_idx = event_idx;
fetch.event_id_buf = event_id?
event::id::buf{event_id}:
event::id::buf{};
if(!event_idx)
{
fetch.valid = false;
return fetch.valid;
}
const string_view &key
{
byte_view<string_view>(event_idx)
};
auto &event
{
static_cast<m::event &>(fetch)
};
assert(fetch.fopts);
const auto &opts(*fetch.fopts);
if(!fetch.should_seek_json(opts))
if((fetch.valid = db::seek(fetch.row, key, opts.gopts)))
if((fetch.valid = fetch.assign_from_row(key)))
return fetch.valid;
if((fetch.valid = fetch._json.load(key, opts.gopts)))
fetch.valid = fetch.assign_from_json(key);
return fetch.valid;
}
//
// event::fetch
//
decltype(ircd::m::event::fetch::default_opts)
ircd::m::event::fetch::default_opts
{};
//
// event::fetch::fetch
//
/// Seek to event_id and populate this event from database.
/// Throws if event not in database.
ircd::m::event::fetch::fetch(const event::id &event_id,
const opts &opts)
:fetch
{
std::nothrow,
index(event_id),
event_id,
opts,
}
{
if(!valid)
throw m::NOT_FOUND
{
"%s not found in database",
string_view{event_id}
};
}
/// Seek to event_id and populate this event from database.
/// Event is not populated if not found in database.
ircd::m::event::fetch::fetch(std::nothrow_t,
const event::id &event_id,
const opts &opts)
:fetch
{
std::nothrow,
index(std::nothrow, event_id),
event_id,
opts,
}
{
}
/// Seek to event_idx and populate this event from database.
/// Throws if event not in database.
ircd::m::event::fetch::fetch(const event::idx &event_idx,
const opts &opts)
:fetch
{
std::nothrow,
event_idx,
opts,
}
{
if(!valid)
throw m::NOT_FOUND
{
"idx %zu not found in database",
event_idx
};
}
ircd::m::event::fetch::fetch(std::nothrow_t,
const event::idx &event_idx,
const opts &opts)
:fetch
{
std::nothrow,
event_idx,
m::event::id{},
opts,
}
{
}
/// Seek to event_idx and populate this event from database.
/// Event is not populated if not found in database.
ircd::m::event::fetch::fetch(std::nothrow_t,
const event::idx &event_idx,
const event::id &event_id,
const opts &opts)
:fopts
{
&opts
}
,event_idx
{
event_idx
}
,_json
{
dbs::event_json,
event_idx && should_seek_json(opts)?
key(&event_idx):
string_view{},
opts.gopts
}
,row
{
*dbs::events,
event_idx && !_json.valid(key(&event_idx))?
key(&event_idx):
string_view{},
event_idx && !_json.valid(key(&event_idx))?
event::keys{opts.keys}:
event::keys{event::keys::include{}},
cell,
opts.gopts
}
,valid
{
false
}
,event_id_buf
{
event_id?
event::id::buf{event_id}:
event::id::buf{}
}
{
valid =
event_idx && _json.valid(key(&event_idx))?
assign_from_json(key(&event_idx)):
event_idx?
assign_from_row(key(&event_idx)):
false;
}
/// Seekless constructor.
ircd::m::event::fetch::fetch(const opts &opts)
:fopts
{
&opts
}
,_json
{
dbs::event_json,
string_view{},
opts.gopts
}
,row
{
*dbs::events,
string_view{},
!should_seek_json(opts)?
event::keys{opts.keys}:
event::keys{event::keys::include{}},
cell,
opts.gopts
}
,valid
{
false
}
{
}
// array of db::cell::~cell()
ircd::m::event::fetch::~fetch()
noexcept
{
}
[[gnu::visibility("hidden")]]
bool
ircd::m::event::fetch::assign_from_json(const string_view &key)
try
{
auto &event
{
static_cast<m::event &>(*this)
};
assert(_json.valid(key));
const json::object source
{
_json.val()
};
assert(!empty(source));
const bool source_event_id
{
!event_id_buf && source.has("event_id")
};
const auto event_id
{
source_event_id?
id(json::string(source.at("event_id"))):
event_id_buf?
id(event_id_buf):
m::event_id(std::nothrow, event_idx, event_id_buf)
};
assert(fopts);
assert(event_id);
event =
{
source, event_id, event::keys{fopts->keys}
};
assert(data(event.source) == data(source));
assert(event.event_id == event_id);
return true;
}
catch(const json::parse_error &e)
{
const ctx::exception_handler eh;
const auto event_id
{
event_id_buf?
id(event_id_buf):
m::event_id(std::nothrow, event_idx, event_id_buf)
};
log::critical
{
m::log, "Fetching event:%lu %s JSON from local database :%s",
event_idx,
string_view{event_id},
e.what(),
};
return false;
}
[[gnu::visibility("hidden")]]
bool
ircd::m::event::fetch::assign_from_row(const string_view &key)
try
{
auto &event
{
static_cast<m::event &>(*this)
};
if(!row.valid(key))
return false;
event.source = {};
assign(event, row, key);
// N.B. a row assignment might not produce an event.event_id unless
// the key is explicitly selected or it was otherwise trivially found.
event.event_id =
{
event.event_id?
event.event_id:
!empty(json::get<"event_id"_>(event))?
event::id{json::get<"event_id"_>(event)}:
event_id_buf?
event::id{event_id_buf}:
cell.at(json::indexof<m::event, "event_id"_>())?
event::id{cell.at(json::indexof<m::event, "event_id"_>()).val()}:
event::id{}
};
return true;
}
catch(const json::parse_error &e)
{
const ctx::exception_handler eh;
const auto event_id
{
event_id_buf?
id(event_id_buf):
m::event_id(std::nothrow, event_idx, event_id_buf)
};
log::critical
{
m::log, "Fetching event:%lu %s JSON from local database :%s",
event_idx,
string_view{event_id},
e.what(),
};
return false;
}
[[gnu::visibility("hidden")]]
bool
ircd::m::event::fetch::should_seek_json(const opts &opts)
{
// User always wants to make the event_json query regardless
// of their keys selection.
if(opts.query_json_force)
return true;
// If and only if selected keys have direct columns we can return
// false to seek direct columns. If any other keys are selected we
/// must perform the event_json query instead.
for(size_t i(0); i < opts.keys.size(); ++i)
if(opts.keys.test(i))
if(!dbs::event_column.at(i))
return true;
return false;
}
[[gnu::visibility("hidden")]]
ircd::string_view
ircd::m::event::fetch::key(const event::idx *const &event_idx)
{
assert(event_idx);
return byte_view<string_view>(*event_idx);
}
//
// event::fetch::opts
//
ircd::m::event::fetch::opts::opts(const db::gopts &gopts,
const event::keys::selection &keys)
:opts
{
keys, gopts
}
{
}
ircd::m::event::fetch::opts::opts(const event::keys::selection &keys,
const db::gopts &gopts)
:keys{keys}
,gopts{gopts}
{
}
ircd::m::event::fetch::opts::opts()
noexcept
{
}