Compare commits

..

479 commits

Author SHA1 Message Date
LJ Sinclair
6222e9eb58
Update README.md
changed links to the community help repo
2024-02-22 10:20:41 +11:00
Коrd Campbell
c31eb2b64e
Update README.md with community 2023-05-30 10:26:47 -05:00
Коrd Campbell
c59a714d37
Create OPENSOURCE.md 2023-05-30 10:24:36 -05:00
Seebs
6383a96ac5 treat Percentile as an error if we can't use PQL Percentile
If we can't successfully generate a PQL Percentile call, error
out rather than implementing an actual Percentile function in SQL.
This can be revisited if anyone needs it.
2023-04-07 15:52:26 -05:00
Seebs
c658e771b0 make percentile work on Decimals, also make Percentile slightly better
So there's a lot going on here.

Percentile just did not work, even a little, with decimals.

In theory we try to make the int val part of ValCount work, in
ValCountize, but you can't actually use that for everything because
it unconditionally adds bsig.Base even when it shouldn't. But it
doesn't matter that we were returning those values from, say,
(Field).MinForShard, because ValCount.Smaller was not preserving them
when identifying the smaller of two Decimal ValCounts anyway.
And even if it did, the logic in Percentile wouldn't have worked
with passing the raw unscaled integer in as a value to compare
against.

But that's fine because the logic was also more generally wrong.
According to the existing logic, a value is the median value if
exactly as many values are less than it as are greater than it.

This is... not actually very accurate to what we usually mean by
"median". Because some values are *equal* to a given value. So
for instance, say you have the values {1, 1, 1, [a million 2s], 3}.
Our logic would regard 2 as being too high to be the median, because
3 times as many values are lower as are higher.

New interpretation: Imagine a sorted list of all your values, with
N entries. You want the Nth percentile, which is to say, you want N%
of values to be less than the vale you pick, and (100-N)% to be greater.
You can round both of these down. So for instance, if you have 6 values,
and want the median, you want 3 values greater, and 3 values less. To
be picky, we could demand the average of those middle two values, but
we're not in a good position to do that in this implementation.

If the number of desired things less than, or greater than, a target
is 0, we can short-circuit to the minimum or maximum value. This can
happen when nth is close to an end and the number of things is small,
not just at nth=0/nth=100.

So we rework this, and we rework the tests for this behavior to reflect
that logic.

We change executePercentile to be able to return a nil rather than
a weird ValCount in cases where there's no result, such as when
there's no values to compute a percentile of.

We also change the SQL tests to match the new behavior, since some
of them were expecting everything done on a decimal field with values
10-13 to come back as 10.00 as a decimal because that is what the
code returned.

We also propagate these changes to DAX, and along the way, fix up a
TODO item in the DAX copy, and stop skipping the test that was
failing because of that TODO item.
2023-04-07 15:52:26 -05:00
Seebs
7cf2c5b07e handle integers as comparisons for decimal fields
It's reasonable to allow "where x > 13" on decimal
fields. Handle at least int64 and float64.

Once this is up, we find that aggregates can return
non-values, such as nil, in some cases; for instance,
`percentile(x) where x > 13` can yield a nil if x is
never greater than 13, rather than making up a value
from zero data points. So we accept nil as a valid
result type in PQL aggregates.

As a result of this, change two tests which were
unintentionally testing for an arcane edge case bug
in which (1) we can't render a condition to PQL,
such as because you specified an integer for a decimal
field, and (2) the filter is using an aliased name,
in which we would end up failing to generate a PQL
filter, but *also* losing the SQL-layer filter, and
produce wrong results as though there were no filter.

We also alter the tests to use `o.price > 9`, because
this lets us generate three user names, but only two
distinct user names, so the test using DISTINCT returns
a different value than the test not using DISTINCT,
which helps us verify that it's actually working and
not just lucky.

As part of fixing that, there was an intermediate
state where we rejected as an error any case where
generating the PQL filter failed. This broke 21 more
test cases, but in all of those cases, the SQL filter
was actually working.

... But in two of them, we SHOULD have been able to
generate PQL, because they were testing bools for
null, which works fine. We just had a list of
field types we allowed null tests against and
omitted bool because I forgot that bool isn't always
just treated as a kind of mutex.
2023-04-07 15:52:26 -05:00
Seebs
0412a505c9 Pass filters down to Percentile correctly
When pushing an expression down to PQL Percentile, if we have
a filter, it has to be passed as the argument "filter", not as
an additional child argument. We don't need to pass in `All()`
as a filter if there's no filter, Percentile works fine with
no filter provided.
2023-04-07 15:52:26 -05:00
Pat Okeeffe
2bdc30c4f0
fix regex generation (#2377) 2023-04-07 14:10:16 -05:00
David Kagan
24a45bc30d
Cloud 1475 (#2371)
* working on incorporating regex logic

* Implemented a validation check for database name with given rules in doc within controller

* fixing tests to pass

* reflecting changes to match docs

* fixed tests further, hopefully

* for sure fixed integration tests, and moved validation check

* integration tests passed, go test now will pass

* fixed name size to 230 due to previous commit acknowledgement

* fixed field test negative validations

* added missing comma
2023-04-07 15:08:07 -04:00
Pat Okeeffe
7f75193cf2
tidy up show tables behavior (#2374)
* tidy up show tables behavior

* made cli integration test whole again

* Update fbsql \d meta-command to show system tables (#2376)

---------

Co-authored-by: Travis Turner <travis@molecula.com>
2023-04-07 12:58:09 -05:00
Adrian Walker
3b142af2c7
CLOUD-1456: SERVERLESS - CREATE DATABASE (#2375)
statement without a units qualifier causes it to be set to 0

Co-authored-by: Adrian Walker <adrian.walker@molecula.comm>
2023-04-07 12:44:45 -05:00
Pat Okeeffe
c619b7d94e
implemented query hints (flatten) (fb-2124) (#2373)
* implemented query hints (flatten)

* improved testing
2023-04-06 17:28:24 -05:00
Seebs
c66d392c87 uncomment old LIMIT tests, make them pass
We forward-ported a handful of tests from the previous parser
which relied on LIMIT clauses, but then we didn't support that.
Now that we do, we uncomment most of these tests, and actually
give them the correct data structures to compare with.

We leave two tests commented out. One was using `limit 10, 5` to
express a limit plus offset, and the other is using `not fld = 1`
as a WHERE clause, but we don't support unary-not to negate
other expressions.

In the process, we discover that converting a SELECT with a
LIMIT clause back to a string has a missing space, and fix that.
2023-04-06 11:49:11 -05:00
Lory Cloutier
875999e30d
Add test coverage to expressionanalyzer.go (#2370)
analyzeExpression - tupleLiteralExpression was covered by something
else between the ticket getting filed and me starting on it.
(*ExecutionPlanner).analyzeBinaryExpression now has increased
coverage for IN / NOT IN. Several bugs got revealed by adding tests;
those tests are commented out but can be re-enabled by whoever ends
up working on the bugs. Tickets are filed.
2023-04-06 09:55:09 -05:00
Travis Turner
ea72396b4d
Remove Node from data model; standardize on Worker (#2366)
* Remove Node from data model; standardize on Worker

This commit does a lot of things, but in general it attempts to simplify
the data model by getting rid of the Node and NodeRole models. Instead,
these will use the Worker model, which itself has individual boolean
fields for role types.

Get rid of roleType in some FreeWorker methods

rename NodeService to WorkerRegistry

simplify the freeworker interface

fix the tests

* Remove DeleteWorker method from workerJobService
2023-04-04 20:20:53 -05:00
Pat Okeeffe
284f62dcb9
create model, create function... all the goodies (#2264)
* create function, create/drop model; re-introduced limit; added COPY; var(); corr()

* review feedback
2023-04-04 17:44:29 -05:00
Seebs
c8c88ab0ee don't panic on failed table creation
The attempt to set the TrackExistence option for fields
happened before checking whether the field was created
successfully or not. Credit to Rachith for spotting this.
Bug was introduced with the TrackExistence stuff, but
we apparently never had a test case for invalid min/max
values.
2023-04-03 16:29:21 -05:00
Seebs
2af417d5c2 don't panic on a MIN that isn't a call
parseOperand was assuming that any reference to MIN in a place
where an operand was expected was a call, which it should be,
but it might not be. parseCallExpression panics if it doesn't
find a parenthesis, because it's never supposed to be called
when we don't know we have one.

The test for this is in with MinMaxColumnConstraints, even though it's
actually a test of MinMaxFunctionCalls, because that's where the other
tests involving the special MIN/MAX tokens live.

We also stop checking whether MIN or MAX might actually be QIDENT.
If you use a quoted identifier, we're over in the QIDENT case,
not the MIN/MAX case. If the token was MIN or MAX, it's always
unquoted.
2023-04-03 16:29:21 -05:00
Lory Cloutier
7031f7b968
Fb 2048 (#2363)
* Add test coverage for executionplanner.go
*ExecutionPlanner.mapper does not get tested in the case where its
context gets cancelled. In order to make testing this possible,
I've added a context argument to sql_test.MustQueryRow. If it's
nil, MustQueryRow creates a context for itself just like it always
has, but if a context is provided, it uses that.

* Adds test coverage for ExecutionPlanner.mapper in executionplanner.go
The case where the context gets cancelled mid-query is now covered.
The test is timing-dependent - the cancel call has to happen after
the query has been started but before it finishes, and in just the
right part of MustRunQuery, in order to actually produce a context
cancelled error, and not, say, a query cancelled error. May have to
adjust timing if the current delays don't work in CI testing.

* Addressed review notes
-reordered arguments for MustRunQuery
-moved MustRunQuery out of a goroutine, put the cancel in one
2023-04-03 12:04:41 -05:00
David Kagan
9e67f1dddd
Cluster nodes for serverless (#2336)
* slowly making a serverless systemAPI for ClusterNodes()

* implemented some methods for fb_database_info

* fixed linting

* fixed comments
2023-04-03 11:13:08 -04:00
Vengata Krishnan
b5dfb07118
Improve test coverage for ast components in ast.go (#2355)
*Tests are added to extend coverage for statement, expression and source types and many of the ast helper functions
*For those SQL language elements where ast exists but parsing is not implemented, test coverage is added to test only the ast correctness
*Also, removed timestamp EPOCH related compiler code as they become unreachable after their ast equivalent were removed in a previous PR.
2023-03-31 14:27:18 -04:00
HHans09
52f9703585
fb-2030 - added test cases for Joins in sql3 (#2359)
* fb-2030 - added test cases for Joins in sql3

* test cases for joins

* Revert "test cases for joins"

This reverts commit 1501f7b202.
2023-03-31 11:52:19 -04:00
Travis Turner
8fca15e936
RetryWithTx (#2348)
* First pass at RetryWithTx

* Refactor RetryWithTx to take a writable bool (instead of reads, writes)

* Implment DirectiveMethodDiff

This commit adds support for a Directive to contain only the diffs (as
opposed to the full Directive).

* Update controller tests to allow for DirectiveMethodDiff (over Full)

* Update RetryWithTx to retry on duplicate key constraint.

If two concurrent processes call IngestShard() for the same shard, both
were trying to insert the same job into the jobs table. That resulted in
a duplicate key error from the database. We want to include that error
in the list of errors for which RetryWithTx should retry.

* Remove unused method: Directive.TranslatePartitions()

* Replace query in a loop with a single query

We had a query which was looking to see if a job already existed. That
query was inside a loop, and could potentially generate 256 queries (for
example). This commit replaces that logic so that we use a single query
wiht an `IN ()` clause.

* Convert to directive version-by-address

This commit uses a separate directive version per address. It moves the
version get/increment back inside the buildDirective method so that if
two concurrent processes are building a directive for the same address,
one of them will get rolled back trying to commit the version update.

* Migration for directive version by address

* Add a comment about DirectiveVersion lock/unlock logic

* Remove AddLastWins

* fix linter

* handle error in walkdir

* fix test failures from removing AddLastWins
2023-03-30 20:54:37 -05:00
Seebs
a5dda0cb1c off-by-v error in spelling of versions 2023-03-30 17:34:59 -05:00
Seebs
9460bc9ee4 handle tag-only commits with no hash in version
When doing the release process, we generate version numbers
that have a version tag but don't have a hash. The IDK test against
the expected hash doesn't work in this context. Let's check for an
expected tag first.
2023-03-30 17:00:22 -05:00
rachithrr
0201649848
Adding fbsql binary to featurebase tarball (#2358) 2023-03-30 14:04:22 -05:00
Matthew Jaffee
c8199d765e
update ECR-related Makefile targets to get account ID automatically (#2357)
account ID is based off the AWS_PROFILE currently set in the environment
2023-03-30 13:26:09 -05:00
Bruce Baranowski
ad3f2d8f2d
fb-2040 (#2354)
*InbuiltFunctionsset tests
2023-03-28 16:59:00 -04:00
Seebs
283b00c741 hacky workaround: use locking to quiet race detector problems
So we have a problem which is triggered in part by the race detector,
but which is actually deeper, but also possibly rare enough to be
politely ignored.

The real underlying issue is that sometimes when we have multiple
tests running in CI, multiple instances of the CLI test end up using
the same postgres database backing for some of their DAX stuff. We
have workarounds for this in some places, but not others.

But the *observed symptom* of this is that it can cause a trivial
race detector issue where we have one call to `(*Resource).Lock()`
and another call to `(*Resource).IsLocked()` which aren't synchronized
in any way, so if the race detector spots this, it complains.

We can suppress that very easily by synchronizing these. That does
not solve the other possibly-weird problems, so this may not actually
address the issue, but I think it might reduce the rate of sporadic
failures significantly, which would give us some time to think about
solving the deeper problem.

The underlying design issue is that we're reusing the database name
in postgres for testing. This lets us have bounded growth (one database)
while leaving the database contents up after a failed test (so we can
examine them), then truncating the database during startup if it already
exists. Which works fine if *only one thing runs at once*, which would
be true on a laptop, but in CI, it's sometimes not true. A real fix
for that is complex and requires some rethinking of how we approach
the test stuff, as we don't want unbounded growth, but we also don't
want two copies of the test running at once to see each other, and
ensuring cleanup after a test failure is surprisingly hard.
2023-03-28 11:56:23 -05:00
Lory Cloutier
ad5f1d4eaa
Add test coverage in compilebulkinsert.go (#2353)
Assignment compatibility checking in analyzeBulkInsertStatement is
now tested. This isn't checking the values themselves, it's there
to make sure the structure is correct for mapping values to columns.
2023-03-27 17:01:59 -05:00
Seebs
f4905891d4 unbreak nested joins
It turns out that the problem with nested joins was that we were
trying to cleverly invert them, but that seems to be incorrect and
resulted in incorrect nesting.

The test case for this is
	SELECT * FROM X INNER JOIN Y ON true INNER JOIN Z ON false
this is now parsed as
	(X inner join Y on true) inner join z on false

Which, as it turns out, is the structure that stringizes back to the
original statement.

We were previously parsing it as
	X inner join (y inner join z on false) on true
which stringizes out to a different form, and is also, I think,
just straightforwardly not what we want.

So basically, we had special case code to recognize that we
were doing a join on top of another join, and invert them in
some way, and I have no idea why because that seems not to be
correct, or at least, it produces nonsensical stringizing that
we can't then parse.
2023-03-27 16:56:29 -05:00
Seebs
3f7ae75e17 increase parser test coverage significantly
We now test the tuple-assignment at all, although it's
perhaps confusing because we expect a ()-list of columns
to go with a {}-list of values. We also test a lot more
errors and some more successes, and additional literal types
in mustParseLiteral.
2023-03-27 16:56:29 -05:00
Seebs
d4fb807664 drop unused isHex and IsInteger functions 2023-03-27 16:56:29 -05:00
Garrison Davis
eb0640f175 Force rebuilding idk pilosa images 2023-03-27 13:34:43 -05:00
Seebs
63368d5e03 export git SHA commit for IDK tests
This is a sanity-check after a weird CI failure; we want
to ensure that we're actually getting the expected version of
featurebase. The environment variable here is magic to the
IDK tests.
2023-03-27 13:34:43 -05:00
Seebs
ef078ac5a0 add IDK test for expected featurebase commit
We have had some weird problems that look like IDK was being tested
against the wrong version of featurebase. Add a test which requests
the version, and if an environment variable is set, requires that
the featurebase server agrees with it.
2023-03-27 13:34:43 -05:00
Lory Cloutier
d114680222
Add SQL3 test coverage for expressionagg.go (#2351)
FB-2045
aggregate{Avg,Min,Max}->Update now tested for DataTypeDecimal.
{avg,min,max}PlanExpression->WithChildren now tested.
percentilePlanExpression->{Evaluate,Plan,WithChildren} is not
tested because percentile gets sent directly to PQL rather than
getting planned and evaluated in SQL.
aggregateLast->everything is not tested because Last is not yet
completely implemented.
2023-03-27 12:00:47 -05:00
Seebs
f12587f414 handle null results gracefully in SetContains{Any,All}
SetContains returns null if either of the values it's given
is null. SetContainsAny and SetContainsAll should also do this.
2023-03-24 16:01:09 -05:00
Seebs
2b4d49e502 standardize existence-tracking logic a bit better
We distinguish between "TrackExistence option is set"
and "we are actually doing existence tracking", to avoid
mishaps like accidentally creating an "existence" view for
a BSI field or something like that. This logic was being
done probably-correctly in one place, and ignored or
handwaved in some, so this is an attempt to just make
it more consistent.
2023-03-24 16:01:09 -05:00
Seebs
a3a0de2b0a rework and consolidate view name cleanup
We had two different versions of this, and a comment referring to a third
which doesn't exist, so I've consolidated them and made them slightly
pickier, to avoid problems like the one I ran into developing the existence
tracking where one of these optimistically transformed names it actually
shouldn't have. Now if we don't expect a view name, we yield an error,
rather than silently performing a transformation.

This also implies updating the ImportRoaring_MultiView test to use
two valid view names.
2023-03-24 16:01:09 -05:00
Seebs
54dbeec1af support null/non-null tests for non-BSI fields
There's a lot going on here. First, we were treating "the test is
a Condition" as implying BSI, which it doesn't anymore. Second, the
behavior of conditions was weird and BSI-specific. Third, we had
to propagate these changes and features throughout a bunch of code,
including both the core featurebase code and the DAX replacements/copies
of it, plus the SQL3 layer.

We refactor this so that tests for equality and inequality work for
non-BSI fields, so now if you accidentally use `==` in a Row call
on a non-BSI field, it still works; that's not specific to BSI
fields anymore.

We add a TrackExistence flag to fields, and propagate it through
things like our protobuf code, etcetera, so that we can successfully
create fields. Newly-created fields get this by default, because
we add it unconditionally to them, but the paths that are being
called with existing fields don't add it. So, when we "create"
(really, just load the definition of) a field from something stored
in the schema, we don't add TrackExistence to it, but any path to
creating a new field should.

A time quantum field with NoStandardView will *effectively*
lack TrackExistence.

For sets, mutexes, and time quantums with a standard view, anything
that sets bits will also set a corresponding bit for the record in
a new "existence" view. This allows us to distinguish between an
empty set and a null, and also allows null checks to be constant-time.

When clearing bits, we don't clear existence bits EXCEPT that if
you clear a bit in a mutex, *and the bit actually existed*, we clear
the existence bit. For sets and time quantums, clearing bits never
clears the existence bit.

Deleting records clears the existence bit.

We also add code to the `batch` subpackage to generate suitable
existence field bitmaps and import them. This logic correctly handles
empty sets and nils. The `batch` package does not allow specification
of anything equivalent to clearing a single bit from an existing
record, so we don't have to deal with the mutex complexity in that
case, which is good because it would be impossible.

This requires a number of other subtle changes, such as allowing
new fields to have more than one FieldOption specified for them.

We also drop the handful of implementation bits relating to the
"fullySorted" internal-use-only import flag, which existed only to
support the JSON ingest API, which we've removed.

The most dangerous part of this is that the mutex semantics are
impossible to implement on top of our existing API, because they
require us to know, not how *many* bits we cleared, but which
*specific* bits we cleared. I've implemented this as a new Tx method,
which is almost certainly going to be tech debt one day; if we some
day drop the Import API, we should remove that.

The testing for this is only currently covering the Set/Clear
behavior of PQL, and the Import API. The batch tests haven't been
written yet.

Fields that don't have existence tracking enabled refuse to perform
null/not-null tests. They should also report themselves as having
no null values -- if a record exists, sets in it are considered
empty rather than null.

The SQL3 support requires a number of subtle modifications to both
featurebase and some addon tooling. The essential thing is dropping
the unconditional translation of nil slices to non-nil empty slices
in translateResult, both in the executor and the orchestrator. We
also modify the logic that handles generating results from Extract
calls, to ensure that non-null sets get an empty slice created for
them even if they never have any values assigned.

The expected results for some tests are different now; we expect to
get nil slices, rather than 0-length non-nil slices, for fields which
were never written for a given record. Most tests were not changed.
(In every case, if a test was failing, I actually checked the logic
before changing expected results. This required a lot of tracking down
of edge cases.)

The batch package now rejects as an error attempts to clear single
bits from mutex fields, because so far as I can tell it's simply
impossible to have a roaring import that specifies the correct semantics
there; you can't tell whether to clear an existence bit without
access to the currently-set bits, which the batch API doesn't have.
We already supported the special case of specifying a clear value
of nil for clearing a mutex field; now that is the only allowed
value for a mutex field to have in row.Clears.

We change the logic for fixing up incoming view names (in two places)
to stop assuming that any view in a time field other than "" that does
not have viewStandard as a prefix is a partial time quantum name that
should have "standard_" prepended to it. This allows us to submit
bitmaps for "existence" to time quantum fields and not have them
silently transformed into "standard_existence" because that's what we'd
do with "202203".

We drop the field ClearBits method, which was totally unused.

We drop the sliceDifference function, which was used in a previous
mutex implementation and hasn't been used in ages, and the test
case for it, and the helper function used only by that test case.
2023-03-24 16:01:09 -05:00
Seebs
0dfaddf7b4 fix old typo
At some point in some other refactor, this option got removed from the
function call it was supposed to be an option to.

But actually that was... not correct either.

Because if you look closely, it turns out that this test was completely
broken; we were ignoring the results that were in the test, and using
inline results, but that's okay, because we were also doing the wrong
query for the second test, and ignoring the Field Options specified in
the test... all fixed now.
2023-03-24 16:01:09 -05:00
tgruben
bc07fb4a96
SQL3 Test Coverage: complete coerceValue coverage (#2343)
* complete coereceVal test coverage

* sql between test

* optimized between operator

* basic operator test
2023-03-24 13:33:05 -05:00
David Kagan
dd90838deb
Cloud 1457 (#2347)
* added dupe check

* added dupe check for databases

* prevent dupe table names

* refactored table dupe search

* further refactoring

This commit, further refactoring of checking for duplicates are done with SQL commands
Also adjusted tests to confirm changes

* fixed linting errors

* moved the errors around to keep them consolidated in the dax package

also removed useless comment
2023-03-23 16:20:45 -04:00
Lory Cloutier
fc74c8ecde
Add tests cases for coverage in expressiontypes.go (#2345)
FB-2041
Added tests for typeIsTimeQuantum and typeIsSet and made them pass.
Added tests for DataTypeTuple cases in typesAreAssignmentCompatible.
Timestamp conversion checking is handled before it gets to that
point but I left those branches in as a backstop.
Checking to see if DataType[String,ID]SetQuantum can be assigned
to themselves doesn't appear to be reachable currently but left
those branches in, because something may use them in future.
Added one test to the DAX skip list since it's the IDSetQ version
of a StringSetQ test that was already on there, changed skip list
to refer to both tests by name instead of by number.
2023-03-23 14:17:06 -05:00
Vengata Krishnan
9e39eee9c9
fb-2049 improve test coverage for select statement (#2339) 2023-03-23 11:10:57 -04:00
Travis Turner
82700264e2
fbsql: add the --csv and --pset flags (#2342)
* fbsql: add the `--csv` flag for CSV output in non-interactive mode

* fbsql: add support for the `--pset=VAR[=ARG]` flag
2023-03-22 08:57:28 -05:00
Matthew Jaffee
8fe73146c8
Sqldb rip boltdb (#2341)
* serverless sqldb use same env for test config as normal

* rip boltdb implementation of controller backend out

it was replaced by postgres and no longer works properly.

This involved migrating a number of tests which only worked with
boltdb, which exposed several ways in which the postgres
implementation had slightly different behavior from the bolt
one:
1. ordering of results in some cases, and
2. (more importantly) erroring when a record to delete was not
found. The bolt implementation silently ignored it when things to
delete weren't found, so we make some changes to match that behavior.

Also stopped propagating CreatedAt and UpdatedAt from DB tables into
dax types. These were breaking existing tests. Perhaps it would be
better to actually use them, but for now they will only exist at the
DB level.

This change set also moves the insertion of the directive_versions
record out of migrations and into the startup/connection code. Having
this in the migrations was a bit ugly because you couldn't just
truncate all the tables and have everything work from
scratch. Inserting it during startup is fairly innocuous, and will
just continue on if it already exists.

* update directive_version test

I changed the initial value to 0 so that the first version that gets
sent out is 1
2023-03-22 08:54:13 -05:00
Travis Turner
f5f7c5e551
fbsql: add support for \d meta-command. (#2340)
`\d` will list tables (in the future it will also include things like
views)
`\d tablename` will show info about tablename
2023-03-21 21:10:31 -05:00
Travis Turner
10aab583c9
fbsql disconnect from database with \c - (#2338)
* fbsql disconnect from database with `\c -`

This adds the ability to disconnect from the current database by passing
a hyphen to the `\c` meta-command.

* Update cli/cli.go

Co-authored-by: Matthew Jaffee <jaffee@pilosa.com>

---------

Co-authored-by: Matthew Jaffee <jaffee@pilosa.com>
2023-03-21 19:21:40 -05:00
Travis Turner
2f7ae30784
Add HasDirective to dax.Node struct to force Directive on restart (#2335)
For on-prem serverless, if we restart the process containing the
controller and computer(s), when they come back up, the controller
doesn't know that the computers have been restarted, so it doesn't send
them a directive. This change forces the controller to send a directive
upon startup by a computer.
2023-03-21 13:22:16 -05:00
Vengata Krishnan
2cf972b5d1
fb-2036 improve coverage for create view statement (#2333) 2023-03-21 14:06:38 -04:00
seebs
0e70d80030
stop suppressing IDK tests, fix IDK test for DAX (#2334)
DAX now relies on Postgres, so we use the Postgres which is already in
the IDK tests (for the external lookups thing) to use as the
controller's metadata store as well. The env variables are a little
confused, but I'll clean that up separately.
2023-03-21 10:28:42 -05:00
Bruce Baranowski
ca99d47249
Add SQL3 tests - /planner/inbuiltfunctionsstring.go (#2331)
* scalar string function test expansion
2023-03-20 17:04:27 -04:00
Seebs
117cbd6590 retry thing that depends on something asynchronous
There's no actual way to forcibly sequence our check of the history
until after the history has been updated, but it's pretty fast usually,
just not always instantaneous. Without this, adding a few millisecond
delay in the tracker reliably produces the test failures we kept seeing
with an unexpectedly low length of 3. With this, it passes consistently
even with the artificial delay.

The query tracker being asynchronous is probably fine, but we need
to test it as though it might take a while.
2023-03-20 12:41:37 -05:00
Vengata Krishnan
693ea1c3a0
fb-2056 improve test coverage for alter table statement. (#2332) 2023-03-20 13:02:37 -04:00
Vengata Krishnan
72871e6e5d
FB-2054 - improve create table timestamp column type coverage (#2330) 2023-03-20 13:01:23 -04:00
Matthew Jaffee
be4f365eaf
Cloud 1358 bolt postgres (#2286)
Switch Serverless from using BoltDB to Postgres as metadata store.

Previously, the controller stored all metadata to BoltDB. This implements SQLDB (currently Postgres flavored) as the backing store for metadata. This will allow us to have multiple instances of the controller running for HA, and to easily inspect and repair the contents of the metadata store.

Unfortunately, it was not straightforward to keep the BoltDB implementation working alongside the SQL one, so it will be removed in a later patch. Once that's done, the SQL implementation should allow for a number of simplifications of the schemar and balancer interfaces.

Database migration is built directly into the application by embedding the migration files and logic from the `soda` command line tool. When connecting to the RDBMS, the app will always attempt to create the necessary database and apply any outstanding migrations.

Integration tests truncate all tables upon start, but *not* at the end, so the state of the database can be inspected after integration tests.

Had to refactor some of the controller's background tasks to make sure they get properly shut down on controller exit.
2023-03-20 09:02:22 -05:00
Vengata Krishnan
2c3be9d1e8
Fix failing selects on views defined with date literals (#2313)
* Fix failing selects on views defined with date literals
* System variables implementation.
2023-03-17 12:24:00 -04:00
Garrison Davis
9c082c5c77 Increase golangci-lint timeout to 8 minutes
This is the time being used in our other CI pipeline.

The alternative to doing this is ripping out all of this code so that
we're not running two CI pipelines...
2023-03-17 09:59:09 -06:00
Lory Cloutier
b729348c02
Add SQL3 test for oppqlgroupby.go (#2326)
FB-2027
The DataTypeIDSet and default branches in groupByColumns weren't getting tested.
Added tests to make sure they are now covered.
2023-03-16 14:58:19 -05:00
tgruben
7ae2f0225b
add test coverage for ordring by string, bools, and timestamps (#2327) 2023-03-16 14:51:57 -05:00
tgruben
c9c63b22b4
Add SQL3 Tests added bulk insert tests (#2324)
* added bulk insert tests

* test idset,stringset,bool in parquet

* bulk insert time coverage
2023-03-16 13:02:39 -05:00
Jacob Brinlee
ada48be181
check for valid name in kafka-config (#2321) 2023-03-16 09:16:31 -05:00
David Kagan
c10762220a
renamed 2 system tables (#2310)
* renamed 2 system tables

* adding table column for types

* added a type field to fb_database_nodes system table

* updated ClusterNode struct

* adding backwards compatibility

this commit also adds support for ordering systemTables and implements the method

* fixed linting

---------

Co-authored-by: Travis Turner <travis@molecula.com>
2023-03-15 17:43:11 -05:00
tgruben
f514474014
Test Row Append (#2323) 2023-03-15 13:13:44 -05:00
tgruben
e02ea2c2e7
SQL3 tests newMessageError and all the constructors (#2320)
* sql3 wire protocol message constructor tests

* convert to testify assertion, clarify comment
2023-03-15 12:41:35 -05:00
Travis Turner
aa17b8d725
Enable linter: stylecheck (#2317)
* Enable linter: stylecheck

This enabled the stylecheck linter, but excludes some staticchecks for
now. The following are ignored because they will take a bit of time to
address, but the intention is to address them and remove them from the
exclusion list.

ST1000: at least one file in a package should have a package comment
ST1003: golang naming standards
ST1008: error should be returned as the last argument
ST1016: methods on the same type should have the same receiver name
ST1020: comment on exported function

* Address ST1015

For some reason this failed in CI but not locally. I can't figure out
why that check isn't happening locally. This just moves the switch
statements around so that the `default` is the first (or last) item.

* Adjust error string in test to match case-adjusted error

* Remove TestCloseTimeout
2023-03-14 08:45:18 -05:00
Travis Turner
a8c2ff603d
Add CSV support to fbsql (#2318)
* Pre csv cleanup

* Implement the CSV writer

This adds the `format` sub-command to `\pset` in order to choose between
formats `aligned` and `csv`.
2023-03-13 17:41:08 -05:00
Travis Turner
37ee6ea482
fbsql integration test framework (#2308)
* stub out a test framework for fbsql

* Introduce fbsql integration test framework.

This also adds support for `pset location` to set the geo location (i.e.
time zone) in which timestamps should be displayed. And it adds support
for comments (lines starting with `--`) in the line reader/splitter.

* Replace Stdin and Stderr with setters and un-export them
2023-03-13 16:29:18 -05:00
Jacob Brinlee
cd32cd7696
handle empty avg agg (#2316)
* handle empty avg agg
2023-03-13 12:59:38 -05:00
Travis Turner
c79cc3b7db
linter: prealloc (#2315) 2023-03-11 21:19:05 -06:00
Travis Turner
d2856bfeee
Linters! (#2314)
* Add (commented out) linters that we should introduce

I went through the available linters and added (commented out) the ones
I think we should work on in the near term. In other words, fix them,
then uncomment them so they are enabled in CI.

* linter: errchkjson

* linter: ineffassign

* linter: gosimple

* linter: errname
2023-03-10 15:13:15 -06:00
Lory Cloutier
6de130fe39
Added date_trunc time/date scalar function (#2312)
FB-1961
Added function and test coverage.
2023-03-10 14:49:47 -06:00
rachithrr
ef14f3a560
FB-1894: Implement DateTimeDiff() (#2307) 2023-03-10 10:12:27 -06:00
Lory Cloutier
b17582110f
Change datepart function to datetimepart (#2303)
FB-1897
The specs for the datetimepart function are, as far as i can tell,
identical to the existing datepart function. Per Pat, replaced the
datepart function with datetimepart rather than just adding
datetimepart as an alias. Made sure existing tests that were using
datepart got switched over.
Second go at this after sorting out weirdness with git.
2023-03-09 13:04:38 -06:00
Jacob Brinlee
1f829f26b3
FB-1905: Test Consumer Close Timeout (#2229)
add config and closetimeout testing to Kafka consumer
2023-03-08 21:49:24 -06:00
Vengata Krishnan
4d484641f2
Gracefully handle divide by zero (#2306)
Divide by zero in SQL expressions will be reported as SQL errors.
2023-03-08 16:51:28 -05:00
Andrea Cappelletti
ecda941aac
Add platform specification when building binaries (#2302)
* Add platform specification

* Add new rule with platform specification

* Reformat code

* Refactor indentation and typo
2023-03-08 11:48:06 -06:00
rachithrr
909c62d44e
FB-1895: Implement DateTimeFromParts (#2296) 2023-03-07 16:47:07 -06:00
tgruben
dc6cbad3fc
compileOrderingTermExpr needs to return alias and not expression (#2300) 2023-03-07 15:56:58 -06:00
Travis Turner
29a5ac971f
Add default fbsql cloud configuration (#2301)
Until we have API token support, connecting to cloud requires the
cognito configuration. This commit adds the production cognito settings
for defaults, and we have intentinally omitted these from the documentation.
2023-03-07 14:52:12 -06:00
Lory Cloutier
7ea4135ecf
Add datetimename function to SQL3 (#2293)
FB-1896
Added the datetimename function, which returns parts of a timestamp
as strings. Month and day of the week are named ("January", "Monday")
while others are returned as a string of digits ("2023").
Added tests to the test definitions.
2023-03-07 12:58:44 -06:00
Travis Turner
05ebdd15f0
Minor cleanup to some fbsql flags and meta-commands (#2299)
* Set fbsql prompt based on the connected database

This also changes the prompt to align with psql, where it begins with:
db=#
and the mid looks like:
db-#

* Require organizationID in on-prem, serverless queries

* Support meta-commands in `--file` command

I'm not sure why this was restricted before. Just an oversight.

* Change default history file name to fbsql_history

* Add port short flag: p

* Support meta command \list (for \l) and \out (for \o)

* cleanup while writing docs

* Have \cd with no arguments change to home directory

* Avoid shadowing `action`
2023-03-07 12:18:55 -06:00
Vengata Krishnan
0708673df5
fb-1893 Adding new scalar SQL function datetimeAdd(timeunit, duration, target) (#2295)
* fb-1893 Adding new scalar SQL function datetimeAdd(timeunit, duration, target)
2023-03-07 11:16:00 -05:00
Travis Turner
eb6c6e3105
Add kafka support to CLI (fbsql) (#2278)
* Add kafka support to CLI (fbsql)

This commit adds the ability to provide a `--kafka-config` command line
argument referncing a toml file to configure kafka.

* Move "Molecula Consumer" message to the logger; hide it in basic mode

* Fold decimal(scale) into kafka.source-type

* Build fbsql with cgo in docker for CI

* Re-organize the fbsql kafka config and setup.

Allow field config to use the table schema if no fields provided.

* Display timestamp fields with format RFC3339Nano

* remove kafkaRunner (no longer used)

* Fix cli/batch test (and make sure it's not excluded from CI)

The logic in our Makefile was exluding from tests any package with
`/batch` in the package name. This excluded `/cli/batch`, which is not
good.

This commit changes the exclusion logic to include the `/v3` portion of
the package name, so `/v3/batch`.

* Rename Basic() to SetBasic()
2023-03-07 08:18:22 -06:00
Seebs
244d80753e reuse clients instead of making new clients
Buckle in, this one's a ride.

This is attached to the same PR as a fix for exiting abruptly
during some tests because I ran into that issue, and comprehended
it, while trying to track down weird and sporadic test failures
that were actually this issue.

The actual, underlying, problem: `make test`, by running all the
tests at once, was hitting a bug that was mostly effectively
triggered by running the `dax/test/dax` tests, and the top-level
`featurebase/v3` tests, at the same time. However, the interaction
was nothing as obvious as temporary files, etcd configuration,
or whatever.

We were running out of port numbers.

The tests were using a bit over 30k simultaneous established TCP
connections, each to different ports, because we were creating
new clients for basically every single operation. For instance,
in a single SQL test that did an import and then a read, we
were creating a new client for each field written to, and then
also creating a new client for each field in results that needed
key translation. And none of these clients were closed or
timed out in any way. In fact, Go doesn't really *do* "closing"
of clients; the closest is that an http.Client can be told to
close idle connections that it has been keeping open.

The worst offenders were both named `fbClient`, and were nigh-identical,
except one of them was implemented as a method on `importer` in the
IDK tree, and one was a standalone function.

It may seem surprising that the method on `importer` is using a shared
client pool for all importers, rather than a new pool for each
importer. This is because we potentially make quite a few importers
during tests.

Before this, running either of the dax tests or the top-level
tests would show well over ten thousand simultaneous ESTABLISHED
connections. After this, the dax tests used nearly twenty.

The problem with port consumption like this, while more noticeable
on MacOS, is also something we could hit on the CI runners, especially
if a single runner ended up with more than one test suite running
at the same time. This probably manifests as sporadic very strange
failures of CI, with messages about "cannot assign requested address".
(Note that an outgoing connection to a successfully-created port
requires *another* port to be assigned for the outbound socket.)

This was complicated dramatically by the fact that, for some
utterly cursed reason, it was *especially* common for the point
at which we hit this, in the top-level featurebase tests, to be
running one of the backup tests in TestVariousQueries, and
specifically, to be hitting it on the dataframe part of the
backup... Which is to say, on the *one* path in the backup function
that called log.Fatal, and thus terminated the featurebase process
abruptly without further commentary.
2023-03-06 13:12:22 -06:00
Seebs
f3abd11884 don't use things that instantly exit in a code path tests hit
The testing package is full of subtle magic, and one of the most
subtle is this: t.Logf, etcetera, all write to a buffer which is
then displayed after the test is run. Which means that, if you
exit, the buffer is never displayed. This means that, if a test
case can fail in a way that causes an instant exit, you don't
hit defers, you don't get your log messages, you just get a mysterious
exit of the process.

We have two cases where backup commands were calling log.Fatal
instead of returning an error. The error in question is displayed
correctly and informatively if returned, so we return it.

We also have one case where we were using os.Exit to avoid a
deadlock. Instead, we make the thing that would deadlock
conditional on the test not having failed. In the event that
the test fails, we now print our failure message correctly,
then also report an unclosed cluster. That's fine.
2023-03-06 13:12:22 -06:00
jacob
549566b6c2 kafka delete functionality 2023-03-06 09:52:31 -06:00
jacob
33d05e6267 limit parallel testing 2023-03-06 09:52:31 -06:00
jacob
e9796e1aed adding kafka delete functionality 2023-03-06 09:52:31 -06:00
jacob
aad32f1dbd adding kafka delete functionality 2023-03-06 09:52:31 -06:00
dependabot[bot]
c4b0e1e1fb
Bump minimist from 1.2.5 to 1.2.8 in /lattice (#2289)
Bumps [minimist](https://github.com/minimistjs/minimist) from 1.2.5 to 1.2.8.
- [Release notes](https://github.com/minimistjs/minimist/releases)
- [Changelog](https://github.com/minimistjs/minimist/blob/main/CHANGELOG.md)
- [Commits](https://github.com/minimistjs/minimist/compare/v1.2.5...v1.2.8)

---
updated-dependencies:
- dependency-name: minimist
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-03 16:09:14 -06:00
tgruben
dba15669c6
fb-1915 Support large id's in NDJSON (#2290)
* uses json.Decoder to allow for large integer values in ndjson format in bulk import
2023-03-03 15:37:56 -06:00
Garrison Davis
7b8b3d8e4f Remove pre_clone_script 2023-03-02 17:24:43 -07:00
tgruben
cbbaba98cd
Use json.Number decoder to handle large ints in sql wire protocol (#2285)
* Use json.Number decoder to handle large ints in sql wireprotocol
2023-03-02 14:49:55 -06:00
dependabot[bot]
4c0bb3b7c1 Bump golang.org/x/net from 0.2.0 to 0.7.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.2.0 to 0.7.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.2.0...v0.7.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-02 13:21:48 -06:00
Garrison Davis
5c22c9803a Add default retries 2023-03-02 11:55:02 -07:00
Garrison Davis
ffdb308472 Remove manual git clean from CI 2023-03-02 11:55:02 -07:00
Garrison Davis
f0e1b72834 Add CI_PRE_CLONE_SCRIPT to .gitlab-ci.yml 2023-03-02 11:55:02 -07:00
Pat Okeeffe
9386fc75b2
implement select from time quantum columns (fb-1654) (#2282)
* first time quantum queries working

* implement select from timequantum columns

* skip a dax test

* make linter happy

* addressed review feedback

* reverted over eager test elimination
2023-03-02 00:06:58 -06:00
seebs
b35c240da7
handle count(*) in having correctly (#2274)
* correct reference for `having count(*)`

It turns out that `having count(*) ...` was always treating
the count(*) as exactly 1. After studying this a lot, I noticed
that in fact, we correctly handle other counts. The reason is
that there's already code to recognize aggregates in `having`
clauses as matching aggregates that are being computed -- but
it only covers the other aggregate clause types, not the newly
added `countStarPlanExpression` from making `count(*)` work even
if there's no `_id` field.

We add several corresponding test cases.

* fix sum(a_decimal) type conversion

Added a test case for this, and also added a fix for it.
Underlying issue: qualifiedRefPlanExpression could end up
producing an int64 instead of a pql.Decimal, even though it
had expected type Decimal.

Originally this worked by politely converting an int64 to
a pql.Decimal in the Evaluate phase, but this was not ideal;
the real question is why it was coming out as an int64 at
that step. Showed this to Pat, who spent a while studying it
and produced a better fix.

* temporarily comment out test which fails in DAX
2023-03-01 23:49:45 -06:00
Fletcher Haynes
a479441ea2 Reverted some testing log messages and commented out code 2023-03-01 21:43:53 -08:00
Fletcher Haynes
21bc76ddb2 Removed cobra option to ignore parse errors of flags since we are using MarkDeprecated 2023-03-01 21:43:53 -08:00
pokeeffe-molecula
11e6d2d9a5 this now prints a message 2023-03-01 21:43:53 -08:00
Fletcher Haynes
660428d5fb This changes the server sub-command to ignore unknown flag. Fixes FB-2019 2023-03-01 21:43:53 -08:00
Vengata Krishnan
6a3c47dbe1
fb-2013 removing sql feature flag entirely. (#2283)
Make SQL endpoint always available.
2023-03-01 15:12:04 -05:00
seebs
b7e9879526
forward-port tests from SQL1 tree (#2261)
This forward-ports a number of tests from the previous SQL
implementation. The porting is approximate in a number of ways,
and not all tests are implemented/tested yet.

In particular, several tests are currently disabled because
we don't support `limit n` constructs.

The tests that were primarily tests of the parser have been
brought forward as parser tests. One of them has been altered
to add parentheses, because our parser interprets
	fld1 between 1 and 3 and fld2 = 2
as:
	fld1 between (1 and 3) and (fld2 = 2)
which is invalid, while the old parser apparently interpreted it
as:
	(fld1 between 1 and 3) and (fld2 = 2)

We have not yet verified the SQL spec's requirements here, but
sqlite agrees with our old parser, not our new parser, so this
may be a regression.

The old tests expected an INNER JOIN to suppress duplicate
values. Our new code does not, which is consistent with other
SQL implementations. This is a change, but the old behavior
appears to have been wrong. (You can still suppress duplicate
values by specifying DISTINCT.)

In the previous implementations, a value like `count(*)` had
`count(*)` as its column name. In the new implementation,
it has an empty string as its column name.

Related to this, the prior implementation allowed you to
write
	select age, count(*) from grouper group by age having count > 1
but the new implementationt requires that to be spelled as
	having count(*) > 1

This is consistent with other SQL implementations, so I think
the new behavior is correct.

The behavior of SHOW COLUMNS and SHOW TABLES has changed, in
that the specific results returned are significantly different.
Perhaps more significantly, the old system spelled the former
query as SHOW FIELDS, rather than SHOW COLUMNS. This may be
considered a regression, in that `SHOW FIELDS` no longer works,
and we should consider whether any hypothetical users might
have been relying on the output of either of these. (I hope
not, the new output is much better.)

Some of the old tests (the ones in handler_test) were accommodated
by adding a couple of specific test cases to existing tests,
specifically:
	* handling timestamp values with `Z` rather than `+00:00`
	* a join with a WHERE clause referring to fields in both
	  source tables

We introduce a new "partial" comparison type, because there's
no way for a test of `SHOW TABLES` to contain a correct table
row, because `SHOW TABLES` includes timestamps from when tables
were created. I'm not sure this is the right way to do this.

We add corresponding changes to dax_test, because the DAX tree
tests against the SQL tests.

We change the returned types of field names and field types to
plain strings, ironically because DAX needs this -- the test code
in the DAX tree is getting them back as plain strings, rather
than as dax.FieldName and dax.BaseType.

The tests using `having` are commented out because they don't
seem to be working, a ticket has been filed for this.

Two of the tests that should return strings are instead returning
untranslated integer IDs, but only for DAX, not for the regular
SQL tests, and the `delete` test has been commented out for
DAX-specific errors. If we merge this, the next step is to
ticket those and address them separately.
2023-03-01 13:40:50 -06:00
Julio Martinez
b8e5e1f32c
Use curl instead of find so errors propagate. (#2227)
Co-authored-by: Julio Martinez <julio.martinez@featurebase.com>
2023-03-01 11:33:44 -08:00
Jacob Brinlee
ebb3c8a290
SUP-297 (#2265)
* log net/http with TLS and verbose only
2023-03-01 12:58:14 -06:00
David Kagan
f8e21b2798
SQL tests now that CodedErrors are across HTTP (#2284)
* 3 todos in delete_database

* forgot to remove some test options
2023-03-01 13:51:14 -05:00
Seebs
70f92bc038 actually yield checksums to caller
While fixing a bug that log messages were ending up
in the output buffer for backups, we fixed up a bunch of
things to do with log messages and output for various
commands.

Due to a subtle oversight, this means that since we did
that, executor_test's `chkSumCluster` has been dutifully
printing `hash:blahblahblah` to os.Stdout, and returning
an empty string.

This also, indirectly, fixes a very strange behavior
we've had ever since then, which is that a lot of test
output silently disappears. The reason is probably,
although I haven't found the right code path, that we
were ending up closing os.Stdout.
2023-03-01 12:48:00 -06:00
Seebs
841ef32545 force count to 1 to disable test caching
The caching of test results means that we get instant cached results
rather than actually running tests in some cases, which is virtually
guaranteed not to be what we want. There's almost no cases where
this actually speeds a thing up validly, and a lot where it speeds a
thing up invalidly.
2023-03-01 12:48:00 -06:00
David Kagan
d7c6258f16
Cloud 1359 errors across http (#2279)
* WIP: json marshal coded errors for http

* WIP: trying to see how best to implement the http-error tests

* finish stubbing out the Schemar methods in the test

* using json to move CodedErrors across boundaries and associated tests

* implemented feedback and fixes

---------

Co-authored-by: Travis Turner <travis@molecula.com>
2023-02-27 16:34:51 -05:00
Matthew Jaffee
933767ec07
move closing of profiling stuff to Close method so it runs for duration of process (#2277) 2023-02-27 10:41:53 -06:00
Vengata Krishnan
6777e3dc07
FB-1968 timestamp data type related fixes and enhancements (#2256)
* Removed support for EPOCH column constraint from TIMESTAMP SQL data type.
* Implicit conversion of integers to timestamp will treat the integer value as seconds since unix epoch.
* Add new ToTimeStamp(num, timeunit) SQL scalar function to help convert integer values to timestamp.
2023-02-24 14:57:17 -05:00
tgruben
dbea305638
add a batch size to limit upload payloads (#2275) 2023-02-24 12:30:57 -06:00
Garrison Davis
186da6b302 Refactor build-fbsql and upload to S3 in CI 2023-02-24 10:48:48 -07:00
tgruben
c294bc70dc
limit memory for backuptar/restoretar (#2270) 2023-02-23 18:35:52 -06:00
Travis Turner
528ebc93db
CLI variables (#2263)
* Add meta-commands \set and \unset (for variables)

* WIP: first pass at variable replacement

* Use a mapReplacer instead of having Command implement replacer

* remove circular reference with variables

* remove the `replacer` interface; just have it be a struct

* use a lexer for variable replacement
2023-02-22 15:52:58 -06:00
Travis Turner
a633b72f3d
Separate the featurebase and fbsql make build targets (#2272)
* Separate the featurebase and fbsql make build targets

Using the same build target was problematic because they shared the same
flags. Since the `-o` output flag was used, the fbsql binary was
overwriting the featurebase binary.

* make sure the make package target builds fbsql
2023-02-22 15:33:11 -06:00
Seebs
d1dbcabb3d fix cleanup logic for ramdisk usage
we want to be sure we delete the files we created during the
run even if the test run panics, but not files other runs
may have created also in /mnt/ramdisk.
2023-02-22 14:45:56 -06:00
Seebs
8724eb09b0 improve ramdisk config in Makefile, use it for everything in tests
So, we were special-casing creating a ramdisk, and setting a special
environment variable for it, for boltdb translate files, to improve
performance.

But actually, etcd and test cluster data and so on all go in $TMPDIR,
and if you move all of those also into a ram disk, you get way better
performance. But 2GB may not be enough for that.

So!

We unify on $TMPDIR, we bump the default size to 4GB, we make the
size configurable, and we stop using the name RAMDISK. This should
improve performance on MacOS significantly for `make test` and
things like it, and also simplifies our lives by not having a special
case for the boltdb translate files.

Also change the environment variable names used in our CI config.
(I don't see where we mount the ramdisks, but I think that's happening
in our setup.)
2023-02-22 13:19:06 -06:00
Jacob Brinlee
df7e813f01
SUP-302 (#2243)
* add ability to use Θ in all field names & with PQL
2023-02-22 11:30:14 -06:00
jacob
26747362e1 clean up time quantum test 2023-02-22 11:18:06 -06:00
jacob
67d247a479 adding time quantum testing 2023-02-22 11:18:06 -06:00
jacob
3b2111b31c adding some test data 2023-02-22 11:18:06 -06:00
jacob
864c6ad4e7 adding test avro to PDK for recordTime 2023-02-22 11:18:06 -06:00
jacob
bf37dfa9ba add name field to recordTime field 2023-02-22 11:18:06 -06:00
Julio Martinez
f65f7ffe95 Fix bad FLAGS env var passed when building to package. 2023-02-21 13:28:26 -06:00
Julio Martinez
7a839f2e8f
Build target also builds fbsql, fbsql is also packaged. (#2260)
Co-authored-by: Julio Martinez <julio.martinez@featurebase.com>
2023-02-21 08:55:27 -08:00
Lory Cloutier
d0e4012025
Fb 1975 (#2254)
* Store version-check file in the configured data-directory

This also fixes what I think is a bug.
It also un-exports everything.

I have questions.

* Version checking: clean up code, add server flags
FB-1975
Cleaned up version checking, removed a race condition, and added error checking.
Added server flags for check-in endpoint and UUID storage file.
Incorporates Travis's changes to store UUID file in data directory
and unexport most of verchk.go.

---------

Co-authored-by: Travis Turner <travis@molecula.com>
Co-authored-by: seebs <seebs@molecula.com>
2023-02-17 13:50:45 -06:00
Pat Okeeffe
e755fecf63
ORDER BY ....what now!? (fb-1954) (#2257)
* can now order by columns not in the select list

* added testing coverage
2023-02-17 13:35:36 -06:00
Pat Okeeffe
c749e07d03
add support for time quantum inserts with explicit timestamps (fb-1558) (#2262)
* added support for time quantum inserts with explicit timestamps

* fixed copy pasta
2023-02-17 12:06:46 -06:00
Seebs
66e079f1e9 task pool: avoid race condition on shutdown/close
When we close a task pool, we use a condition variable to wait for
workers to exit, if any workers are still running. The workers,
in turn, use the condition variable to notify that they've exited.
Unfortunately, the workers aren't using the lock (the rationale was
that it's all atomic ops so they don't need to), which means that
it's possible to have the following sequence:

	Close(): obtain current live count
	worker: decrement live count
	worker: send broadcast to condition variable
	Close(): wait on condition variable

To resolve this, we make the worker update also request the lock.

We add a simple reproducer for this. Note that simple doesn't mean
it fires completely reliably; on my laptop, the test causes a test
timeout about 60% of the time without the fix. If you add a short
delay between sampling the live count and waiting on the condition
variable, the deadlocks move from "60% chance of hitting it in
a million trials" to "nearly always".
2023-02-17 11:32:07 -06:00
Matthew Jaffee
41a6b9e823
controller commit to DB first then send directives (#2259) 2023-02-17 09:12:15 -06:00
Pat Okeeffe
74885c9718
make count(*) not depend on _id (#2258) 2023-02-17 08:46:25 -06:00
Travis Turner
393721c0ce
Add viper (for env variable) support to CLI (#2251)
* Add viper (for env variable) support to CLI

* Move the "featurebase cli" sub-command to its own "fbsql" command

I don't know if this is the final name, but putting it here as a
placeholder for now.

* Handle single `--command` flags.

This also adds a printer interface so we can opt NOT to print setup
information in non-interactive mode.

* Add support for multiple `--command` flags in the same call

* Add support for multiple `--file` flags

* Move members related to Config into a separate struct

* Make sure non-interactive mode can connect to a database

* comment fix

* support control-C on readline

* Prevent connection message from printing in non-interactive mode

* Return errors (instead of printing them) in non-interactive mode
2023-02-16 20:41:39 -06:00
tgruben
4172976e6a
Manual buffer management in restore-tar (#2249)
* manual buffer management
reuse buffer on backuptar as well
test coverage for backuptar
2023-02-16 14:57:00 -06:00
tgruben
6a9843b4a0
use primary as source for FieldTranslate backup data (#2255)
* use primary as source for FieldTranslate backup data
2023-02-16 12:13:11 -06:00
Pat Okeeffe
41b8505d70
Aggregation Nation (fb-1955, fb-1887) (#2252)
* make nodeid come from the correct table

* refactored aggregates; added ability to aggregate on expressions not just references

* addressed feedback

* now with the compiler errors fixed after rebase
2023-02-16 10:56:27 -06:00
Seebs
10b60f5d51 set default epoch for timestamps in system tables
If we don't set an epoch, we get a cryptic message on the console.
Note, this message isn't logged properly, it doesn't use the
logger, it uses the `log` package.

	2023/02/09 11:07:23 ERROR: converting timestamp options for
	end_time: checking overflow: custom epoch too far from
	Unix epoch: 0001-01-01 00:00:00 +0000 UTC

Because this uses the log package, it doesn't go to the same place
as other messages, making it a pain to debug.

The underlying problem is that a timestamp can't just have a zero
value for its epoch. So, we set a default epoch of 0 Unix Time.

We should possibly revisit the question of whether the conversion
in the top-level schema.go should handle an epoch which IsZero,
but I'm not sure what "base" should be in that case. In practice,
all existing usages except this one are specifying time.Unix(0, 0)
already.
2023-02-15 14:34:51 -06:00
Seebs
f4e1e63dd0 fix typo in file name 2023-02-15 14:34:51 -06:00
Seebs
5dffbccdff add test coverage, fix bugs caught by added test coverage
Note: We now skip a test because we can't pass it but fixing
it is presently beyond my understanding. In parser_test.go,
we skip
	SELECT * FROM X INNER JOIN Y ON true INNER JOIN Z ON false
because if we stringify it, we put the "ON true" in the wrong
place, and end up with something we can't parse.

The main change here is modifying AssertParseStatement and
AssertStatementStringer (and the corresponding Expression
functions) to also verify that they can clone and round-trip,
and that we can walk expressions. This gives us a ton
more coverage of Clone and conversions to string, and caught
a number of subtle typos and missing type switch cases.

Some of the changes are mostly cosmetic, such as only
including optional words when stringifying expressions
or statements if those optional words were present originally,
as shown by the Pos value stored for those words.

We also drop a lot of trailing apostrophes from some of the
parse test cases, which appear to be harmless but won't be
reproduced when converting back to strings.

We also add a number of additional test cases, or add
clauses to existing test cases, to improve coverage of a
lot of error testing. For instance, we added a decimal
field to the tests of show table, and added cases
using KEYPARTITIONS. (Although it doesn't *do* anything.)

Similarly, whenever we create a statement, we check
the behavior of requesting a list of sources from it, to
verify that source finding code at least runs.
2023-02-15 14:34:51 -06:00
Seebs
d6ec9649fc mark test helper as helper
If this fails, it'd be nice to know which invocation of it failed.
2023-02-15 14:34:51 -06:00
Pat Okeeffe
f205459003
fixed issue with interplay between count(*) and _id column (#2250) 2023-02-14 18:40:15 -06:00
Gregory Throne
6bf693b98c
docs migration link changes (#2247) 2023-02-14 17:28:08 -06:00
Bruce Baranowski
5c6361918a
FB-1862: Implement Str() scalar string function (#2215)
* Implement STR()
2023-02-14 15:58:36 -05:00
Pat Okeeffe
3dcc55203f
(fb-1903) - Fix SQL Fanout error (#2248)
* added some debug code

* added more logging

* do sql fanout on dedicated endpoint
2023-02-14 10:54:15 -06:00
Travis Turner
87011e4294
CLI: make it more like psql (#2235)
* Refactor CLI to mimic psql's meta-commands

This PR adds support for meta-commands (also known as "backslash
commands") like those in psql, Postgres's CLI. Only a few meta-commands
are currently implemented, but this was meant to demonstrate how we
could use something like `\i file.csv` to insert local files into SQL
statements.

* Meta-commands: \file and \include

The initial implementation used `\i` as a streaming file handle.

This commit changes that to `\file`, and then implements `\i` (or
`\include`) as handling multiple sql commands.

* Add meta-command "help" (\?)

This is basically a copy of the psql help output, but includes only
those options we currently support.

* Add support for \o [file], and \timing

The \o meta-command writes query output to a file.

The \timing meta-command turns on/off the timing display sent to stdout.

* Add meta-commands: \l (show databases) and \dt (show tables)

* Add meta-command: \watch [period]

* Update meta-command \connect to take database name instead of ID

* Add support for \echo, \qecho, and \warn

This commit contains an known issue in that the `-n` option will exclude
the line feed, but if the output is the terminal, the readline package
clobbers any content on the current line (i.e. anything without a line
feed). That will need to be addressed at some point.

* Add support for \w [FILE] (write query buffer to file)

* Add SchemaAPI no-op implementation

* Refactor query handler to align with /sql and /databases endpoints

We want to standardize on:
/sql
/databases/{databaseID}/sql

* Add CLI support for expanded, border, tuples_only (and pset)

* Add help text for \pset and \t
2023-02-14 09:23:51 -06:00
Pat Okeeffe
51f7a41e6c
handle empty strings as nulls for CSV (except for string types) (#2246) 2023-02-13 17:02:04 -06:00
Pat Okeeffe
4261c60a17
added test for inserts with timestamp + constraints (#2244) 2023-02-13 16:15:59 -06:00
Pat Okeeffe
5c74b64722
A bug fix roundup (#2242)
* fb-1940 re-implemented some changes that got missed private-public

* fb-1939 fixes to between + decimals

* fb-1935 - avg() on and id type + fixed some tests

* fb-1953 add min/max for string types

* fb-1938 - remove internal_type column from show columns

* fb-1964 - fix space_used in fb_cluster_nodes to be int

* fb-1996 - make sure all Idents that are being used as object references to schema objects are lowercased

* fixed failing test

* added some missed changes

* fb-1969 found another case issue with identifier used for column idents
2023-02-13 12:28:34 -06:00
Travis Turner
e803a000b8
Use the correct epoch when converting field.Options.Base to timestamp (#2241) 2023-02-10 17:13:09 -06:00
Travis Turner
bc450a91ea
Get rid of *most* of the context.Background() references in sql3 package (#2238)
* Thread context through sql3 DATABASE operations

* Get rid of *most* of the context.Background() references
2023-02-10 13:40:46 -06:00
Travis Turner
126be915a9
Support for setting individual DatabaseOptions (#2231)
* Implement Schemar.SetDatabaseOption(option, value string)

This replaces the temporary `SetDatabaseOptions()` method, which
replaced the entire DatabaseOptions struct, with `SetDatabaseOption`
which takes an option/value pair of strings to set.

* Add SetDatabaseOption to controller http handler and client

This commit also:
- renames some `writeLog` to `writelog`
- updates ApplyDirective to call resource.Unlock() on any resources
  being removed from the local worker

* Add Database related methods to SchemaAPI interface

Currently all implementations of this interface are implemented with
"unimplemented" errors on those methods. Next will be to implement the
necessary methods.

* SQL: CREATE DATABASE and SHOW DATABASES

* SQL: DROP DATABASE

* SQL: Add UNITS option to CREATE DATABASE

* SQL: ALTER DATABASE

* User serverlessStorage.Remove[*]Resource instead of resource.Unlock()

* Add WITH keyword to CREATE/ALTER DATABASE

* fix some WITH logic

* linter fixes

* WITH on CREATE DATABASE is not required
2023-02-06 08:50:28 -06:00
Matthew Jaffee
903e234c69
tweak a bunch of logging and config (#2234)
* tweak a bunch of logging and config

make overall logs less verbose and chatty

1 minute computer check-in interval

3 minute snapshot interval

remove CaptureLogger as it has same functionality as buffer logger

add a WithPrefix to the Logger interface so sub-services can have
different prefixes

* fix some lint

* fix lint... confused why this is coming up now
2023-02-03 14:59:07 -06:00
Matthew Jaffee
b43c4aabc5
move DD profiling/tracing setup into command where it belongs (#2233)
* move DD profiling/tracing setup into command where it belongs

* add url to http trace, use golang for container image
2023-02-01 16:20:39 -06:00
Fletcher Haynes
69331963da Updated version check to use POST only. Updated version check schema. Now generating a local UUID to submit to version check as a unique ID that persists in a file. 2023-02-01 08:28:28 -08:00
Travis Turner
20429bb9dc
Remove MDS and replace it with Controller (#2219)
* Remove MDS and replace it with Controller

This commit removes the MDS layer (and package) and shifts Controller
package into its place.

* add pprof/fgprof to serverless http router

---------

Co-authored-by: Matthew Jaffee <jaffee@pilosa.com>
2023-01-30 16:54:12 -06:00
HHans09
b12c90fdd1
Fb-1816 : CharIndex str func (#2216)
* rebased and updated

* Updates per review comments

Rebased

* Rebased

* Rebased
2023-01-30 16:08:46 -05:00
rachithrr
188b61b3cd
FB-1817: Implement FORMAT() (#2220) 2023-01-27 11:04:39 -06:00
tgruben
4e45f19ca0
parquet-info command to browse parquet files (#2230)
* parquet-info command to browse parquet files

* null support in parquet
2023-01-26 12:55:54 -06:00
tgruben
90e2808f52
bulk import support for parquet files (#2226)
* bulk import support form parquet files
2023-01-26 08:32:39 -06:00
Matthew Jaffee
49ef905b89
linter directive should not have space (dummy commit to trigger CI) (#2228) 2023-01-25 17:00:23 -06:00
Matthew Jaffee
a397501111
try to make sure we're releasing large allocations (#2225) 2023-01-25 14:48:01 -06:00
Pat Okeeffe
53cd483709
timestamp fixes (#2224)
* stop falling through a missing else + added test

* fix min and max on timestamp + tests

* address review feedback
2023-01-25 14:07:15 -06:00
Pat Okeeffe
d92ea8babf
hand comma version of inner join (#2221) 2023-01-25 13:37:05 -06:00
Matthew Jaffee
8f1f3c6d06
Fix pre sort cmd (#2222)
* make ndjson pre_sort parallel, fix several bugs, test

* remove json tags (not needed), rename pre_sort -> presort
2023-01-25 11:01:58 -06:00
Travis Turner
468461fbcf
Add Drop Database and Drop Table support (#2208)
* Support Drop Table in serverless (include Snapshotter, Writelogger)

* Finish Database methods

Things like:
- `Databases`
- `DatabaseByID`
- `DatabaseByName`
- `DropDatabase`

* Change Poller to use NodeService instead of its own map

Instead of the Poller maintaining its own map of Addresses to poll, this
commit changes the Poller to use the NodeService interface to get all
known nodes from the Controller.

The next commit needs to:
Next, the logic in the boltdb NodeService implementation was moved to
the boltdb Balancer implementation. That way, the Balancer can be the
source of truth for all things nodes/workers/jobs.

* Move NodeService from Controller to Balancer

This commit moves the implementation of the NodeService into the
Balancer, and aligns `Balancer.AddWorker` with `NodeService.CreateNode`
so that they stay in sync. (Same for `Balancer.RemoveWorker` and
`NodeService.DeleteNode`).

* fix import of private repo

* Fix go vet issues

* Fix bug in DeregisterNode

We need to remove the node from the NodeService even if it's not
assigned to a database. The logic had a bug in it.

This also adds some no-op implementations for SnapshotService and
WriteloggerService. If a directory was not configured for that, then the
computer node would panic on trying to read from the Snapshotter upon
receiving a Directive.

* queryer response content-type: json

* Add support for NULL to WriteloggerDir and SnapshotterDir configs

This commit changes the way WriteLoggerDir and SnapshotterDir are
handled.
If value is empty `""`, an error will be returned on computer startup.
If value is `"NULL"`, a no-op implementation of the service will be
used. This would be for a case that wanted to run serverless on-prem
with no durable storage.
Finally, any other value will be used as the directory to use.

Some things which aren't considered here and may result in unexpected
behavior:
- a value with spaces `" "`
- any "null" which is not "NULL"... like lowercase.

* Finish the DropTable test

* Change "disable service" value to case-insensitive "off"

This commit also removes an unnecessary sleep in the tests.

* Fix docker-compose variables for IDK test

Co-authored-by: Matthew Jaffee <jaffee@pilosa.com>
2023-01-23 19:59:47 -06:00
Pat Okeeffe
ad49fb174d
get fb_exec_requests underlying storage to use less memory (#2210)
* only keep the last 2000 requests and truncate sql and plan text to 4K each

* fixed import

* address review feedback

Co-authored-by: Fletcher Haynes <fletcher.haynes@generalassemb.ly>
2023-01-23 17:30:12 -06:00
Pat Okeeffe
bbca86d599
only turn a table scan into a PQL group by if it is a direct child of group by (#2217) 2023-01-23 16:49:31 -06:00
Pat Okeeffe
3606167758
handle float --> stringset implicit map conversion in bulk insert (#2211)
Co-authored-by: Fletcher Haynes <fletcher.haynes@generalassemb.ly>
2023-01-23 16:18:46 -06:00
Garrison Davis
f6baf32dbd Stop go test in GitHub CI
These tests are happening in GitLab CI instead.
2023-01-23 13:48:24 -07:00
Matthew Jaffee
6d4c1d9db1
Sup 294 pre sort command (#2209)
* first cut at pre-sort command that works on ndjson

* finish pre_sort command for CSV and JSON and add test

* try fixing golangci-lint

* remove some dumb lint checks

* more linter disabling

* take .golangci.yml from previous repo

* go fmt (facepalm)

* remove ioutil to fix lint
2023-01-23 12:26:38 -06:00
Garrison Davis
7bd62952d6 Add GitLab CI pipeline 2023-01-23 10:24:52 -07:00
Garrison Davis
39b2c97caa go mod tidy 2023-01-23 10:24:52 -07:00
Garrison Davis
c07727548d Stop using string keys in contexts 2023-01-23 10:24:52 -07:00
Travis Turner
6c0dcc126e
Merge pull request #2214 from FeatureBaseDB/tlt/fix-cli-exit
Fix CLI "exit" command
2023-01-21 16:49:07 -06:00
Travis Turner
cfbfee031b
Fix CLI "exit" command
When we added a line feed to the user input, we broke the check for
"exit" (because after that change we were getting "exit\n". This commit
moves the exit check before the line feed append.
2023-01-21 14:05:06 -06:00
Joseph Friedrich
ceb91cff51
Merge pull request #2207 from FeatureBaseDB/commit-sync-1-19-2022
Commit sync 1-19-2023
2023-01-19 20:19:26 -06:00
Joe Friedrich
7da67caa99 fix go deps, add lattice 2023-01-20 02:11:00 +00:00
Joe Friedrich
9d095ca6c2 Fixed controller import path 2023-01-20 01:06:54 +00:00
Joe Friedrich
c025daa226 Fixed dep paths 2023-01-20 01:01:18 +00:00
Joe Friedrich
82a398980d fix import sync "7f6ea0e6e..c38210bef" 2023-01-19 22:14:06 +00:00
Fletcher Haynes
15eafa9825 Add version checkin (#2413)
* Initial commit of code to do a version check-in on startup

* Add json tag to the response struct for version check

* Adjusted version check response types

* Changed error message in version check-in goroutine to use the logger. Changed URL to prod from dev.

* Updated version checkin URL to be analytics

Co-authored-by: Fletcher Haynes <fletcher.haynes@featurebase.com>
(cherry picked from commit c38210bef5)
2023-01-19 22:10:15 +00:00
Pat Okeeffe
b3a552e0d3 implement left join (fb-1888) (#2420)
* implement left join

* fixed failing test

(cherry picked from commit 7386f13159)
2023-01-19 22:10:15 +00:00
Jacob Brinlee
1046e28338 SUP-288 (#2414)
* adding kafka consumer config options (--kafka-max-poll-interval, --kafka-session-timeout,  --kafka-group-instance-id, --kafka-socket-keepalive-enable, and --consumer-close-timeout)

* wrapping consumer.Close() in timeout. Will wait consumer-close-timeout seconds before forcing consumer to exit

* clean up logs

(cherry picked from commit 17cdc58d80)
2023-01-19 22:10:15 +00:00
Travis Turner
f6a767befb Refactor CLI (#2417)
This commit moves the cli out of the `ctl` package and into its own
`cli` package. It also adds some basic tests for expected input.

Finally, it fixes a bug which was causing intentional line feeds to be
ignored, which was a problem with the BULK INSERT command.

(cherry picked from commit cf72bfa16f)
2023-01-19 22:10:15 +00:00
pokeeffe-molecula
f2812309ac make the count....count (#2416)
(cherry picked from commit b6e642338a)
2023-01-19 22:10:15 +00:00
HHans09
965ad15829 Fb 1876 : Implement Replicate() func (#2410)
* fb-1876 : creating Replicate fun

* fb-1876: creating string replicate func

* Fb-1876: String Replicate func

* Fb-1876: String Replicate func

(cherry picked from commit 2fccb87dcf)
2023-01-19 22:10:15 +00:00
Travis Turner
a9b3fd2c4d Database isolation: Balancer (#2407)
* Database isolation: Balancer

Remove naive Balancer

remove debugging lines

Thread dax.Transaction through Controller

Change role to roleType

Swap out Balancer interface with new one

Standardize InvalidTransaction error

Add some interface comments

* Remove type.Worker; replace with type.Address

* Remove database validate from Queryer

This is already being handled in the `CreateTable()` method. Prior
to doing that validation, we were getting a panic, but that's no longer
the case.

* Remove dax.TableQualifier; replace with dax.QualifiedDatabaseID

* Update IDK test to create database

(cherry picked from commit d971cfc269)
2023-01-19 22:10:08 +00:00
pokeeffe-molecula
5d025e399b implement CREATE/ALTER/DROP VIEW (fb-1592) (#2408)
* implement CREATE/ALTER/DROP VIEW

* fixed failing test

* another failing test

* fixed some broken serverless tests

(cherry picked from commit c620aae350)
2023-01-19 21:51:16 +00:00
rachithrr
fc1b8fdfb8 FB-1827: Implement Len() (#2406)
(cherry picked from commit f99be656df)
2023-01-19 21:51:16 +00:00
tgruben
adbad90fe1 string support for dataframe (#2405)
* String support for arrow

(cherry picked from commit 4cd3cb02a2)
2023-01-19 21:51:16 +00:00
pokeeffe-molecula
a1fc6d04a1 introduce performance counters and system table fanout, plus refactor metrics (#2363)
* performance counters

* first cut of perf counters and system table fanout and a wire protocol
* significantly refactored prometheus support; removed statsd and exprvar

* removed node_id

* put dax subquery test back

* Change Translator.TranslateFieldIDs method to take a dax.TableKeyer

There are a bunch of other calls to the Translator interface methods
with currently take an `index string`, and those need to be converted to
dax.TableKeyer as well. But I need to review each call, because in at
least one place I noticed one being called with `result.Index` instead
of with the qtbl available. And I don't yet know how those could be
different.

Co-authored-by: Travis Turner <travis@molecula.com>
(cherry picked from commit 7f6ea0e6e5)
2023-01-19 21:35:02 +00:00
Коrd Campbell
b6d290487d
update license to full license 2023-01-12 18:23:09 -06:00
Joseph Friedrich
61f9698082
Merge pull request #2201 from FeatureBaseDB/readme-doc-link-fixes
updated links in readme to point to latest pages in docs
2023-01-12 17:42:04 -06:00
Joseph Friedrich
5274caece8
Merge pull request #2204 from FeatureBaseDB/1-10-2023-cherry-pick
v3.27.0 cherry pick from private
2023-01-12 16:05:13 -06:00
Joe Friedrich
475bf58465 resolve go vet errors related to redeclares 2023-01-12 20:32:49 +00:00
Kord Campbell
3a7d6efcda add LICENSE 2023-01-12 09:18:35 -06:00
Joe Friedrich
48178064fe sync cherry-picks 4750c2215..17cb2cbb7 2023-01-12 00:59:13 +00:00
Joe Friedrich
fee6d8553a sync cherry-picks 4750c2215..17cb2cbb7 2023-01-12 00:58:33 +00:00
Коrd Campbell
e2c3b4008c fixes FB-1873 (#2400)
Co-authored-by: Kord Campbell <kord@Bob.local>
Co-authored-by: Fletcher Haynes <fletcher@capitalprawn.com>
(cherry picked from commit 17cb2cbb77)
2023-01-12 00:49:58 +00:00
pokeeffe-molecula
0ddf69a322 fix sum aggregate (fb-1874) (#2404)
* handle sum aggregates with ints; handle escaped quotes in blob literals

* added test ceoverage

* skip subquery test for dax

(cherry picked from commit af475a27f2)
2023-01-12 00:49:58 +00:00
rachithrr
1cdcaf472f FB-1814: Implement ASCII() (#2378)
* FB-1814: Implement ASCII()

(cherry picked from commit f590227471)
2023-01-12 00:49:58 +00:00
Julio Martinez
9030a054b7 Create main log with 640 permissions, leave all other logs the same. (#2402)
Co-authored-by: Julio <julio.martinez@featurebase.com>
(cherry picked from commit 3f91cc3dd1)
2023-01-12 00:49:58 +00:00
Julio Martinez
c23608557b Add dd tracer and associated config options (#2403)
Co-authored-by: Julio <julio.martinez@featurebase.com>
(cherry picked from commit 581a4d19a4)
2023-01-12 00:49:58 +00:00
Joe Friedrich
7acf3265ca fix import paths and import cycles 2023-01-12 00:31:14 +00:00
Joe Friedrich
77305b55ed fix indent 2023-01-11 19:29:01 +00:00
Joe Friedrich
d22bd9430f fix import paths 2023-01-11 18:59:24 +00:00
pokeeffe-molecula
3b1707c568 include space_used column in show table output (#2401)
(cherry picked from commit 4750c2215f)
2023-01-10 23:28:09 +00:00
Lory Cloutier
2b9c13497e Implement SPACE() function for SQL3 (#2399)
FB-1861

(cherry picked from commit da74f0a312)
2023-01-10 23:28:09 +00:00
pokeeffe-molecula
5e151eed0e handle insert into timequantum fields with default 'now' time (fb-1868) (#2398)
* handle insert into timequantum fields with default 'now' time

* allocate on the stack

(cherry picked from commit 18fe6a35f6)
2023-01-10 23:28:09 +00:00
Jacob Brinlee
3b233d271d adding more error handling for rbf (#2395)
* adding more error handling for rbf

Co-authored-by: Jacob Brinlee <jacobbrinlee@Jacobs-MBP.attlocal.net>
(cherry picked from commit c9ce26ce96)
2023-01-10 23:28:09 +00:00
pokeeffe-molecula
a6317c58e0 added space_used columns (#2397)
added space_used columns to show tables and fb_cluster_nodes

(cherry picked from commit 164aac509e)
2023-01-10 23:28:09 +00:00
pokeeffe-molecula
4e43b767df Bug fix round up (fb-1841, fb-1819, fb-1867) (#2396)
* check root operator after optimize

* round of bug fixes

(cherry picked from commit 9f042216a7)
2023-01-10 23:28:09 +00:00
Travis Turner
01cae92d96 Change JSON tag name on WireQueryResponse from execution-time to exec_time (#2394)
* Change JSON response name from exec_time to execution-time

Execution time stopped working in the CLI because it uses the latest
json tag.

* Wait, don't break the interface.

* Add a test for the sql response json tags.

This is to make sure that if someone like Travis just goes and changes a
tag name to be more consistent, that we perhaps catch that before it
gets to the end user.

* Change exec_time to execution-time after all

(cherry picked from commit b5dd3ea02e)
2023-01-10 23:28:09 +00:00
tgruben
5ec31d4159 [FB-1831] distribute bulk insert to owning node (#2391)
* distribute bulk insert to owning node

(cherry picked from commit e8505d8a53)
2023-01-10 23:28:09 +00:00
pokeeffe-molecula
74ee3ebf0e implemented DISTINCT (fb-1562) (#2388)
* implemented distinct

* implemented distinct
* uses first cut of a buffer pool, and extendible hashing with thresholded spill to disk
* tests
* cleaned up some stuff around query plan output to make developing tooling easier
* added optimization to call PQL Distinct()

* fixed test

* fix for passing wrong index name in orchestrator

* back out change to DistinctTimestamp

* fix other instance of wrong table name being passed

* use full index name instead of abbreviated one for translation. sigh.

* removed some unused code

Co-authored-by: Matthew Jaffee <jaffee@pilosa.com>
(cherry picked from commit f030d58d95)
2023-01-10 23:28:00 +00:00
Matthew Jaffee
36f2dcce9e clean up TODOs. adds a control channel for on-demand snapshotting
(cherry picked from commit 4cc1667399)
2023-01-10 23:27:48 +00:00
Matthew Jaffee
e09a9dca47 first cut at removing all the shard/field/partition versioning
some cleanup needed

(cherry picked from commit bc9057f492)
2023-01-10 23:27:40 +00:00
Travis Turner
69a174412d Small adjustments to support the Serverless cloud merge (#2385)
This just changes a make target and the CLI setup. Nothing in
featurebase is actually affected.

(cherry picked from commit 33bc69ccc6)
2023-01-10 23:27:10 +00:00
Matthew Jaffee
0f67a0c432 first cut at automatic snapshotting
- had to make sure we don't snapshot until directive is fully applied
on a computer... otherwise there's races between loading the files and
truncating the write log.

- added a dirty bit to resources and a bool return to incrementing the
write log... don't snapshot if it returns false because that means
there's been no writes. (but make sure you close the storage transaction!)

- added the actually snapshotting routine which just fires every
<timeout> and serially snapshots everything.

- tweaked some logging

- added ability to get all tables in an org/db or literally all. I
think I just needed the "literally all", but it was natural to allow
it to be scoped to org or DB as well.

(cherry picked from commit b8b08bc9eb)
2023-01-10 23:26:29 +00:00
pokeeffe-molecula
f8120d4833 Consistency in error handling (fb-1799) (#2383)
* return 200 once plan compilation starts; if error, return error in response.

* removed some commented out code that is definitely not needed.

(cherry picked from commit 9dda3ff215)
2023-01-10 23:26:29 +00:00
Matthew Jaffee
dfd4bb1191 Expose translation mvcc (#2381)
* expose Transaction on TranslateStore for DAX Snapshotting

* try to fix ramdisk nonsense

apparently, we were running in either a shell env or docker env
randomly, so this could sometimes pass and sometimes fail since the
shell env had the ramdisk set up and docker didn't.

Now we force to run in docker always and set up ramdisk explicitly.

* debug ramdisk issue?

* fix tests... and a buncha other stuff

The executor test I modified failed when I changed DefaultPartitionN
to 8, but just because stuff was out of order so I made it more
robust.

I edited some data gen stuff to make shorter lines because it was
making grep results unusable.

the actual fix is in translate_boltdb_test.go

* clean up, fix code review feedback

(cherry picked from commit 3693b9950a)
2023-01-10 23:25:58 +00:00
pokeeffe-molecula
a6c165dd57 Implement DELETE (fb 1557) (#2382)
* delete implementation with test coverage

* optimize IN expressions; stop linter complaining

* fixed some uncovered query cases

* skip test in DAX for now

(cherry picked from commit 021219935f)
2023-01-10 23:25:08 +00:00
Bruce Baranowski
36c020f076 Fb 1818 Implement PREFIX() and SUFFIX() (#2371)
* Implement Prefix and Suffix
* Update substring out-of-index handling

(cherry picked from commit d19f3e81da)
2023-01-10 23:25:08 +00:00
tgruben
427f7a4494 Force make to use bash (#2380)
(cherry picked from commit 72d03602e0)
2023-01-10 23:25:08 +00:00
Matthew Jaffee
a9b248bb29 "fix" backup tar test by just comparing lengths not byte for byte
(cherry picked from commit 1e2e698225)
2023-01-10 23:25:08 +00:00
Matthew Jaffee
71c0624abb remove in mem translate store
(cherry picked from commit 7ab117f5d1)
2023-01-10 23:23:18 +00:00
Matthew Jaffee
da4a42dfe9 use separate qcx for write/read in test
now that min/max queries don't use a write Tx it seems we need to
separate read and write in the tests. Not sure I 100% understand this.

(cherry picked from commit a58299def4)
2023-01-10 23:22:58 +00:00
Matthew Jaffee
da85614c24 set transactions to writable: false for Max/Min/Value calls. these were mistakenly set to true during cleanup
(cherry picked from commit 53a8f58e91)
2023-01-10 23:22:58 +00:00
tgruben
24372a9405 [FB-1822] Change dataframe disk format to Arrow from parquet (#2376)
* change default backend to arrow file format instead of parquet

(cherry picked from commit 6d96a1474e)
2023-01-10 23:22:58 +00:00
Fletcher Haynes
2993ab5ca1 This fixes a bug with displaying errors returned from the SQL3 endpoint (#2374)
Co-authored-by: Fletcher Haynes <fletcher.haynes@generalassemb.ly>
(cherry picked from commit f65367ab46)
2023-01-10 23:22:58 +00:00
Lory Cloutier
5f15d3ad10 Fix bulk ingest queries on multi-node databases (#2375)
CLOUD-1252
Implemented Jaffee's fix of checking for b.useShardTransactionalEndpoint
and only running the start/finish transaction block if it's false. Moved
stats timing to a separate defer so it could stay out of the if.

(cherry picked from commit b1f5264a4b)
2023-01-10 23:22:58 +00:00
rachithrr
4aa34d3e1c FB-1815: Implement CHAR() (#2369)
(cherry picked from commit 27963441ab)
2023-01-10 23:22:58 +00:00
Travis Turner
99e3fd14d7 Fix PQL distinct in dax (#2360)
* Fix PQL distinct in dax

When issuing a PQL Distinct() call (or any other call with a "index=" arg),
this commit will attempt to convert the value in the index arg with a
TableKeyer.

* Apply change to call.Children as well

* Add some PQL Distinct (join) test coverage

(cherry picked from commit 4e8fe488de)
2023-01-10 23:22:58 +00:00
pokeeffe-molecula
211c3b759d add allow_missing_values option to bulk insert (fb-1823) (#2372)
* add allow_missing_values option to bulk insert

* test coverage

* review feedback

(cherry picked from commit 15d2ee8b07)
2023-01-10 23:22:58 +00:00
pokeeffe-molecula
bb2c805cac handling missing epoch constraint correctly (#2366)
(cherry picked from commit 843312dfc9)
2023-01-10 23:22:58 +00:00
pokeeffe-molecula
f42a33640a enable handling string representations of integers (#2367)
(cherry picked from commit 3528ec8fc0)
2023-01-10 23:22:58 +00:00
pokeeffe-molecula
4ef70c19da SHOW CREATE TABLE issues (fb-1810) (#2365)
* fixed ddl issues with cache type/size; removed shardwidth option; improved error message

(cherry picked from commit c88d60c9ab)
2023-01-10 23:22:58 +00:00
Matthew Jaffee
f6c0cf1112 rename stupid manager names
ManagerManager -> ResourceManager
Manager -> Resource

(cherry picked from commit 033be81799)
2023-01-10 23:22:58 +00:00
Matthew Jaffee
0e8773707a code review tweaks
(cherry picked from commit cf1c9dae9a)
2023-01-10 23:22:58 +00:00
Matthew Jaffee
9a441ef66b remove version/directive stuff from other snapshot endpoints
(cherry picked from commit 4366ad41fb)
2023-01-10 23:22:58 +00:00
Matthew Jaffee
41b865d838 clean up unused code/comments
(cherry picked from commit 3ddf79160f)
2023-01-10 23:22:54 +00:00
Matthew Jaffee
4d678faa72 fix dumb issue on storage manager test
changed empty snapshots/writelogs to return nil which was causing NPE

(cherry picked from commit bee666c07c)
2023-01-10 23:22:03 +00:00
Matthew Jaffee
bc3123ddd4 fix lint
(cherry picked from commit 797b8bc31f)
2023-01-10 23:22:03 +00:00
Matthew Jaffee
6cef68a853 several fixes and debug logging
- check that serverlessStorage is not nil before closing it
- check that we don't already hold a lock on a serverless storage
  Manager before trying to load it. This fixed at least one test failure.

(cherry picked from commit 87d1c31607)
2023-01-10 23:22:03 +00:00
Matthew Jaffee
76682753da implement closing on dax, remove all locks when shutting down
(cherry picked from commit dbb6d53f9d)
2023-01-10 23:22:03 +00:00
Matthew Jaffee
ff3595d759 more WIP
(cherry picked from commit bbaa7dd0f1)
2023-01-10 23:22:03 +00:00
Matthew Jaffee
ed7c6d419e extremely WIP
(cherry picked from commit 35c472a54f)
2023-01-10 23:22:03 +00:00
Matthew Jaffee
71e3c00b46 remove alpha director (unused)
(cherry picked from commit 690a9370e9)
2023-01-10 23:21:58 +00:00
rachithrr
e8ac1a0a7f FB-1812: implement stringsplit() (#2362)
(cherry picked from commit a4f18fb25f)
2023-01-10 23:21:31 +00:00
HHans09
766db38277 fb-1809: SQL3 RTrim & LTrim func for strings (#2361)
(cherry picked from commit c1dbc48fb2)
2023-01-10 23:20:52 +00:00
pokeeffe-molecula
b46a82ea33 added updated_at column to show tables output (#2364)
(cherry picked from commit 9759602f94)
2023-01-10 23:20:15 +00:00
pokeeffe-molecula
75999414a7 implement having; create view experiment (#2357)
(cherry picked from commit eca3168d63)
2023-01-10 23:20:15 +00:00
Bruce Baranowski
838bc2dadb FB-1719: implement SQL3 lower() (#2358)
* Implemented SQL3 LOWER()

(cherry picked from commit 4773aabc4e)
2023-01-10 23:20:15 +00:00
Lory Cloutier
748fdc3741 Prevent file corruption when writing tar backup to stdout (#2344)
* Prevent file corruption when writing tar backup to stdout

FB-1794

Tar backups written to stdout were coming out corrupt. This turned
out to be due to log messages getting written to stdout and ending
up in the tar file. We now check to see if the tar file and the log
are both going to stdout, and if they are, send the logs to stderr
instead.

Testing did not have any kind of consistency or validity check. We
now compare a tar file sent to a file and a tar file sent to stdout
to make sure they're the same. This does not guarantee correctness
but does at least catch this form of corruption.

* trying different index name

Co-authored-by: tgruben <tgruben@gmail.com>
Co-authored-by: Todd Gruben <todd@molecula.com>
(cherry picked from commit a8996a149d)
2023-01-10 23:20:15 +00:00
pokeeffe-molecula
f2442dcd88 fixed csv bugs (#2355)
(cherry picked from commit 2146f407c3)
2023-01-10 23:20:15 +00:00
Travis Turner
9e2dbadb82 Fix dax docker-compose (dc-up) which was broken by ServiceManager (#2356)
(cherry picked from commit e572c8f2c1)
2023-01-10 23:20:15 +00:00
tgruben
a2e14df15c Fb 1874 dataframe-csv-loader featurebase subcommand (#2341)
Embeded dataframe-csv-loader command as featurebase subservice

(cherry picked from commit f2a13c8bde)
2023-01-10 23:20:15 +00:00
HHans09
80626df411 fb-1802 : Trim functionality (#2353)
* fb-1802 : Trim functionality

rebase

* fb-1802 : trim - updated as per review

(cherry picked from commit ac3ffac8e6)
2023-01-10 23:20:15 +00:00
Travis Turner
20d7361566 Make interfaces more specific than "MDS" (#2352)
* Make interfaces more specific than "MDS"

- Introduce `dax.Schemar` interface
- Introduce `dax.Noder` interface
- The rest is generally to standardize on the new interfaces.
- Remove `pilosa.SchemaInfoAPI` interface
- Move `TranslateNode` and `ComputeNode` types from controller to dax package
- Remove `queryer.FeatureBaseImporter`
- Remove `queryer.MDS` interface
- Remove `queryer.Importer` interface
- Identify types using an "MDS" interface and split into Noder/Schemar as necessary
- Changed `Queryer.orchestrator` to a `map[qual]*qualifiedOrchestrator` because we can't share an orchestrator across quals

* Convert orchestrator to use TableKeyer

(cherry picked from commit 14f1930004)
2023-01-10 23:20:10 +00:00
Travis Turner
0127147d69 Thread Owner, UpdatedAt, UpdatedBy through SchemaAPI (#2351)
* Fix "qualifer" misspellings

* Remove `track_existence` and `shard_width` from SHOW TABLES output

* Thread Owner, UpdatedAt, UpdatedBy through SchemaAPI

I took the liberty of renaming "LastUpdatedUser" to "UpdateBy" to align
with "UpdatedAt".

(cherry picked from commit 63cfdb5078)
2023-01-10 23:19:19 +00:00
rachithrr
17188b7a7b FB-1805: implement REPLACEALL() (#2349)
(cherry picked from commit 9548e71f46)
2023-01-10 23:19:19 +00:00
Travis Turner
3ee936ebc8 Introduce TableKeyer interface; use in Execute() calls as "index" (#2350)
This commit introduces an interface called `TableKeyer` which anything that means to represent a "table"
can implement. Examples are `dax.QualifiedTable`, `dax.Table`, and `string` (for legacy pilosa calls
where Execute simply took `index string`).

In the case of `orchestrator.Execute()` and `qualifiedOrchestrator.Execute()`, we are intentionally strict
about which type of `TableKeyer` the respective method accepts. If we find, in the future, this is too
restrictive, we can loosen that; but for now it helps us understand what is expected.

(cherry picked from commit a61d1a9571)
2023-01-10 23:19:19 +00:00
Travis Turner
0cb16f0cf4 Remove trackExistence check in batch (i.e. always build _exists) data. (#2348)
(cherry picked from commit 5110405f2f)
2023-01-10 23:19:19 +00:00
Travis Turner
c7b4e47f10 Move batch.Importer interface to pilosa.Importer (#2347)
* Move batch.Importer interface to pilosa.Importer

In addition to moving the interface, it updates all the methods to use
dax.TableID (for example) intead of a string pilosa index name.

* Change unused onPremImporter methods to no-op.

onPremImporter is a wrapper around API which implements the Importer
interface. This is currently only used by sql3 running locally in standard
(i.e not "serverless") mode. Because sql3 always sets
`useShardTransactionalEndpoint = true`, There are several methods which this
implemtation of the Importer interface does not use, and therefore they
intentionally no-op.

(cherry picked from commit 12d608c80d)
2023-01-10 23:18:59 +00:00
HHans09
9346e26158 Fb:1787 - Clean up (#2339)
* Fb:1787 - Clean up

* fb-1787 : String upper function

* Formatting the files

(cherry picked from commit 57ce7c4c0e)
2023-01-10 23:18:38 +00:00
Travis Turner
c4532124e1 Add Table.Description, Table.CreatedAt, Field.CreatedAt support to SchemaAPI (#2340)
* Thread Table.Description through SchemaAPI

* Thread Table.CreatedAt through SchemaAPI

* Thread Field.CreatedAt through SchemaAPI

(cherry picked from commit 734477aaee)
2023-01-10 23:18:32 +00:00
rachithrr
c39e599086 FB-1800: Implement SUBSTRING() (#2343)
substring(string, startIndex,length).

(cherry picked from commit aa2a62fda0)
2023-01-10 23:18:19 +00:00
pokeeffe-molecula
eb54530de6 handle int-->bool map type conversions; handle single value-->(id|string)set map type conversions (#2342)
(cherry picked from commit 728b1dc9f5)
2023-01-10 23:18:07 +00:00
Fletcher Haynes
2020cef8ac This adds in support to the lattice UI application to use the SQL3 (#2338)
* This adds in support to the lattice UI application to use the SQL3
endpoint. If the `/sql` endpoint returns 404, it will use the SQL1
endpoint. If the `/sql` endpoint is available, it will send SQL queries
to that. It does not try the SQL1 endpoint if the SQL3 endpoint returns
an error processing the query. That is, it is all SQL3 or SQL1.

- Below are the specific changes:
- Adds a file that contains functions for interacting with http services as opposed to just grpc/event-based services. As of this commit, it is only the SQL3 endpoint.
- This adds a variable to track if we are using the SQL3 endpoint or not
- This adds a function to handle the response from the SQL3 endpoint
- Adds a function to eventServices to query the sql3 HTTP endpoint
- Fixed a missing semicolon in grpcServices

Co-authored-by: Fletcher Haynes <fletcher.haynes@generalassemb.ly>
(cherry picked from commit 7ab453e289)
2023-01-10 23:17:53 +00:00
Travis Turner
0985eeb9b1 Convert SchemaAPI interface to use dax.Table instead of pilosa.IndexInfo (#2336)
* WIP: Convert SchemaAPI to be DAX-centric

* Tables(), CreateField()

* CreateTable(), DeleteTable(), DeleteField()

* More cleanup

* Remove the old SchemaAPI

(cherry picked from commit a15783cb49)
2023-01-10 23:17:26 +00:00
rachithrr
a74b5c7008 FB-1795: Implement REVERSE() scalar string function (#2335)
(cherry picked from commit d33bf4811f)
2023-01-10 23:16:48 +00:00
pokeeffe-molecula
58ff14441a
updated links in readme to point to latest pages in docs 2022-12-29 10:34:34 -06:00
Fletcher Haynes
5c39a49285 Sync from private repo to commit 12d608c80d 2022-12-12 09:01:20 -08:00
Travis Turner
5c76ad5e70 Move batch.Importer interface to pilosa.Importer (#2347)
* Move batch.Importer interface to pilosa.Importer

In addition to moving the interface, it updates all the methods to use
dax.TableID (for example) intead of a string pilosa index name.

* Change unused onPremImporter methods to no-op.

onPremImporter is a wrapper around API which implements the Importer
interface. This is currently only used by sql3 running locally in standard
(i.e not "serverless") mode. Because sql3 always sets
`useShardTransactionalEndpoint = true`, There are several methods which this
implemtation of the Importer interface does not use, and therefore they
intentionally no-op.

(cherry picked from commit 12d608c80d)
2022-12-12 09:01:20 -08:00
HHans09
682f240b7b Fb:1787 - Clean up (#2339)
* Fb:1787 - Clean up

* fb-1787 : String upper function

* Formatting the files

(cherry picked from commit 57ce7c4c0e)
2022-12-12 09:01:20 -08:00
Travis Turner
830b2ab4c8 Add Table.Description, Table.CreatedAt, Field.CreatedAt support to SchemaAPI (#2340)
* Thread Table.Description through SchemaAPI

* Thread Table.CreatedAt through SchemaAPI

* Thread Field.CreatedAt through SchemaAPI

(cherry picked from commit 734477aaee)
2022-12-12 09:01:20 -08:00
rachithrr
12ff18bc55 FB-1800: Implement SUBSTRING() (#2343)
substring(string, startIndex,length).

(cherry picked from commit aa2a62fda0)
2022-12-12 09:01:20 -08:00
pokeeffe-molecula
40d292b589 handle int-->bool map type conversions; handle single value-->(id|string)set map type conversions (#2342)
(cherry picked from commit 728b1dc9f5)
2022-12-12 09:01:20 -08:00
Fletcher Haynes
bf0486503a This adds in support to the lattice UI application to use the SQL3 (#2338)
* This adds in support to the lattice UI application to use the SQL3
endpoint. If the `/sql` endpoint returns 404, it will use the SQL1
endpoint. If the `/sql` endpoint is available, it will send SQL queries
to that. It does not try the SQL1 endpoint if the SQL3 endpoint returns
an error processing the query. That is, it is all SQL3 or SQL1.

- Below are the specific changes:
- Adds a file that contains functions for interacting with http services as opposed to just grpc/event-based services. As of this commit, it is only the SQL3 endpoint.
- This adds a variable to track if we are using the SQL3 endpoint or not
- This adds a function to handle the response from the SQL3 endpoint
- Adds a function to eventServices to query the sql3 HTTP endpoint
- Fixed a missing semicolon in grpcServices

Co-authored-by: Fletcher Haynes <fletcher.haynes@generalassemb.ly>
(cherry picked from commit 7ab453e289)
2022-12-12 09:01:20 -08:00
Travis Turner
4e3856348c Convert SchemaAPI interface to use dax.Table instead of pilosa.IndexInfo (#2336)
* WIP: Convert SchemaAPI to be DAX-centric

* Tables(), CreateField()

* CreateTable(), DeleteTable(), DeleteField()

* More cleanup

* Remove the old SchemaAPI

(cherry picked from commit a15783cb49)
2022-12-12 09:01:20 -08:00
rachithrr
e33426d0cf FB-1795: Implement REVERSE() scalar string function (#2335)
(cherry picked from commit d33bf4811f)
2022-12-12 09:01:20 -08:00
pokeeffe-molecula
5e28a3424c you should be able to cast an id as a string (#2334)
(cherry picked from commit e599f12ee4)
2022-12-12 09:01:20 -08:00
rachithrr
891a42f9fc FB-1739: Add ability to add a description to a table on creation (#2332)
* FB-1739: Add ability to add a description to a table on creation

- Added CommentOption to handle text after COMMENT option.
- added description field in the createtable plan.
- The description is stored in the existing index metadata.

(cherry picked from commit ad350c2d49)
2022-12-12 09:01:20 -08:00
Travis Turner
ce32a1bde6 Rename some interfaces. Remove the ComputeAPI (#2333)
* Clean up dax service interfaces

Rename some of the `computer` interfaces and organize them in the
appropriate files.
Remove `dax/computer/alpha` package

* Remove ComputeAPI (it was replaced by batch.Importer)

* add nss-tools dependecy to smoke test

(cherry picked from commit 969bf055b2)
2022-12-12 09:01:20 -08:00
pokeeffe-molecula
8fab5239b8 handle decimal without scale correctly; handle bulk insert dupe columns correctly; handle decimal->string & float->string type conversions in bulk insert (#2331)
(cherry picked from commit d2eba5bd8d)
2022-12-12 09:01:20 -08:00
pokeeffe-molecula
c439d53584 enforce int min/max constraints on insert (fb-1772) (#2325)
* moved the debug code to the right spot

* enforce int min/max constraints on inserts

* add a check for decimal min and max

* fixed borked tests

* fix the decimal to int conversion in constraint check

Co-authored-by: Travis Turner <travis@molecula.com>
(cherry picked from commit e392ce3460)
2022-12-12 09:01:20 -08:00
pokeeffe-molecula
c304991e9f implemented extract ddl; tightened up type related stuff (#2329)
* implemented extract ddl; tightened up type related stuff

* added some test coverage

* review feedback

(cherry picked from commit f62313762c)
2022-12-12 09:01:20 -08:00
Travis Turner
a44b622aa0 Introduce ServiceManager and Refactor DAX Integration tests (#2320)
* Introduce ServiceManager and Refactor DAX Integration tests

The ServiceManager provides an interface with which to manage
featurebase (dax) services (mds, queryer, computer). It replaces the
confusing interface implementations in /dax/server/server.go (which
optionally used pointers to in-process objects to satisfy an interface)
with (for now) http implementations. The thought is that even if we're
running all services in-process, we should communicate between services
over http in order to mirror what we would do in a production
environment where the services are running on different nodes.

This batch of commits does quit a lot, most of which is captured here:

- Added `path` support to `dax.Address`. Address is now a string of the form [scheme]://[host]:[port]/[path].
- Added `Holder.directiveApplied` to determine (in tests) if the computer has completed applying the latest directive. This is somewhat temporary until we improve the mds-to-computer logic.
- Removed the "service prefix" code which was prepending client URL paths with the prefix. Instead, the serviceType (mds, queryer, computer[n] is now part of `dax.Address`).
- Removed, from the dax config, the top level `StorageMethod` and `StorageDSN` and now just have `MDS.Config.DataDir`.
- Added `Computer.Config.N` to specify the number of computers to run in-process.
- Moved the `pilosa.MDS` interface to `computer.Registrar`. This is an example of getting the interfaces defined in the right packages.
- Added `SnapshotTable()` method to the mds client (to align with its API).
- Changed `Balancer.AddJob()` to `Balancer.AddJobs()` to support, for example, adding 256 partitions in a single call. Refactored some of the naive Balancer to account for this.
- Added a `Seed` to the top-level config. It's not really useful because of package `crypto/rand`.
- Added an in-memory implementation of the DisCo interface and disabled etcd in a computer service.
- Create sepearte data-dirs for each in-process computer.
- Disabled grpc in dax.
- Modified the sql3 test definition format to support multiple insert steps and separate query results (to align with those steps).

* Changes necessary to get multiple computer instance running in-process

For now the config looks like this:

```
[computer]
run = true
n = 4
```

but we can probably just change that to be something like:

```
[computer]
run = 4
```

*Issues found running multiple "computers" in-process*
- grpc was trying to bind on the same port
  - changed GRPCListener from `*net.TCPListener` to `net.Listener`
  - created a nopListener and set to that for now (i.e. disabled grpc)
- etcd was starting more than once
  - changed dax to use in-memory implementations of the disco interfaces (i.e. stop using etcd)
- IDAllocator (which uses boltdb) was trying to open the `idalloc.db` file more than once
  - realized we have to set separate data-dirs for each holder. that fixed it.

* Port dax integration tests to ManagedCommand

* Modify Balancer-related methods like AddJob to AddJobs

There were (and still are) a lot of places where we were adding on job
at a time, even when we had a long list of jobs to add. This resulted in
every job add (for example adding 1 of 256 shards) taking ~40ms, or over
10s to create a keyed table. One reason was because each job add was
making multiple boltdb transactions.

* Port over more dax integration test stuff

* Add DirectiveApplied to signify that snapshot/writes have loaded.

We use this in tests to avoid using sleeps.
This should be considered temporary; we're going to need a more robust
solution for determining when a computer node is ready to serve complete
data.

* Finish porting dax integration tests

* Improve godocs

* Remove docker-based DAX integration tests.

* go mod tidy

* Move test/managed.go to avoid package conflicts

* Modify IDK integration tests to work with ServiceManager changes

This is really just computer -> computer0
And the MDS DataDir config change.

* cleanup found during review

* echo $CI_COMMIT_REF_SLUG in CI

* remove docker image arg, use build instead

(cherry picked from commit 2843f218bc)
2022-12-12 09:01:20 -08:00
pokeeffe-molecula
d6d5ddb501 non-sql aggregation, top, decimal and sundries (#2328)
* fixed a bunch of issues with non-pql aggregation; moved some decimal related functionality; made top actually top (for the non-pql case); experimental create function

* drive up test coverage

(cherry picked from commit 0be0c42b66)
2022-12-12 09:01:20 -08:00
pokeeffe-molecula
dca0dd84e3 implement fb_exec_requests system table (#2327)
implements an fb_exec_requests system table. The purpose of this table is to allow access to internal state to see what queries are running and have been run.
Co-authored-by: Travis Turner <travis@molecula.com>

(cherry picked from commit 47d8be26f5)
2022-12-12 09:01:20 -08:00
Garrison Davis
daf6e29b02 Make SOURCE_DATE_EPOCH changes for idk
(cherry picked from commit be619fa60d)
2022-12-12 09:01:20 -08:00
Garrison Davis
d0ea451bcc Use SOURCE_DATE_EPOCH to make reproducible builds
Reproducible builds are something we should be doing, and we are there
as far as making them in CI is concerned with this change.

The changes to the Dockerfile/Makefile do nothing if the
SOURCE_DATE_EPOCH environment variable is not set before `make build`
happens, or if the build arg is not passed in to docker.

(cherry picked from commit fd93fc99c9)
2022-12-12 09:01:20 -08:00
pokeeffe-molecula
c210aaba48 produce a better error when a user tries to sort something unsortable (#2324)
(cherry picked from commit 1ce3a1c103)
2022-12-12 09:01:20 -08:00
pokeeffe-molecula
aaf963c9bd (fb-1779) make optimizer smarter with top operators and aggregate queries (#2323)
* updated optimizer to be smarter when trying to push a top operator down; added test coverage

* skip a dax sql test that keeps failing

(cherry picked from commit e0d6b292bc)
2022-12-12 09:01:20 -08:00
pokeeffe-molecula
11aaeb72b4 fixed error messages for alter table add and drop; added test coverage (#2322)
* fixed error messages for alter table add and drop; added test coverage
* Removed two CI tests that are failing intermittently for no known reason.

(cherry picked from commit 3c2c8c6011)
2022-12-12 09:01:20 -08:00
Lory Cloutier
5693767ba2 Don't use reflect.DeepEqual to compare errors.
FB-1771

In api_test.go, it was being used to make sure that incorrect input
produced the right errors; this is now handled by making sure the
error isn't nil and then checking its string against the expected
error string.

In executor_test.go and internal_client_test.go, it was being used
to compare QueryResponse structures, which contain an error.
handler.go now has a function specifically for comparing them,
which can provide additional detail if necessary.

Added a test for SameAs to handler_test.go.

(cherry picked from commit 775fd0b08c)
2022-12-12 09:01:20 -08:00
tgruben
ab4ce354c4 [FB-1776] support for marshaling ExtractedIDMatrixSorted (#2319)
support for marshaling ExtractedIDMatrixSorted

(cherry picked from commit 04b19c066a)
2022-12-12 09:01:20 -08:00
pokeeffe-molecula
eae8376181 Tighten up ORDER BY (fb 507) (#2318)
* tighten up checks for order by expressions fixed ordering by expressions

* added testing to cover order by cases

* Add DecimalAgg member to proto GroupCount definition

In DAX, where we have split the orchestrator from the executor, and the
orchestrator can run on a different host, there are cases where
`GroupCount`s can travel over the wire via the Internal Client. In these
cases, when the group count contains a decimal aggregate, we need to
send that value as the appropriate type.

* fixed missing cases in order by and case block eval

Co-authored-by: Travis Turner <travis@molecula.com>
(cherry picked from commit 158cc669d9)
2022-12-12 09:01:20 -08:00
Lory Cloutier
b6ae088f24 FB-1766: cleaning up the CmdIO objects passing alternate stdin/
stdout/stderr around

A lot of functions in the cmd and ctl packages were passing these
around and barely using them. Replaced them with a logger for most
functions. Some functions get an io.Writer instead so that their
tests can find the output they're looking for.

More cleanup on fb-1766: reworked the tests that were using io.Pipe
or os.Pipe to check their results so they now use a bytes.Buffer.

Unexported some variables that didn't need to be exported.
Fixed NewConfigCommand to use the provided stderr, not os.Stderr.
Added tests for rbf_dump, rbf_page, and keygen, since those weren't
being tested at all.

Added chksum_test, final cleanup.

(cherry picked from commit f627199acb)
2022-12-12 09:01:20 -08:00
tgruben
fa162d16cc upgrade to immutable 0.4.0(generics) (#2317)
(cherry picked from commit a98b9144cb)
2022-12-12 09:01:20 -08:00
pokeeffe-molecula
41e231504e handle filters on _id columns (fb-1765) (#2313)
* handle filters on _id columns using ConstRow
* handle keyed and un-keyed _id columns
* tests!

(cherry picked from commit df829c592f)
2022-12-12 09:01:20 -08:00
pokeeffe-molecula
0bd17d6185 fixed top(x) where top cannot be pushed down into pql query (#2311)
* fixed top(x) where top cannot be pushed down into pql query

* review feedback

(cherry picked from commit d3f10be743)
2022-12-12 09:01:20 -08:00
Travis Turner
b700346a1f Fix more of the SQL tests in dax (#2310)
There are now only four tests remaining which do not pass.

One is related to error format mismatch.
Two require orchestrator work.
One won't pass until table name conversion is supported for multiple
tables.

(cherry picked from commit 5bf5b5364d)
2022-12-12 09:01:20 -08:00
Travis Turner
e2758005cb Prepend header bytes to WriteLog messages for backward compatibility (#2309)
MarshalLogMessage serializes the log message and prepends additional encoding
information to each message. Currently, we prepend three bytes to each log
message:
byte[0]: encodeVersion - this is currently a constant within the code. If we
modify structs such that they encode differently, we'll have to change the
constant and keep previous versions of structs for deserialization.
byte[1]: encodeType (e.g. "json", etc.)
byte[2]: logMessageType

If we get into a situation where we want more flexibility in these message
header bytes—for example, if we want to use more than three bytes—we could do
something with the first bit of the encodeVersion: if it's 1, that could
indicate that there are additional header bytes, and the following seven bits
could indicate how many.

(cherry picked from commit 6740bc250e)
2022-12-12 09:01:20 -08:00
Travis Turner
444d4804ec Fix formatting in CLI results with custom SQLResonse.UnmarshalJSON (#2305)
* Fix formatting in CLI results with custom SQLResonse.UnmarshalJSON

When I started this, it was meant to be a quick fix to address the confusing
result formats we were seeing in the CLI. For example, all large integer values
were displayed in scientifc notation. This is because we were passing the result
types from JSON (in this case, float64) into pretty print. Similarly, `IDSets`
and `StringSets` where being printed using the default go Stringer for the types
[]int64 and []string respectively.

I started by writing a customer UnmarshalJSON() method for the `SQLResponse`
type. Part of this (the part which converts data types based on header types)
was already being used in dax tests, so this just formalizes that logic as part
of the `SQLResponse` type.

Then I realized that the sql3 tests (run against the `sql3` package) were
failing because sql3 is not actually returning the `IDSets` and `StringSets`
types. A future task is to formalize return types, define them, and modify sql3
to return them. Once that is done, we can remove the "typed" switch in the
`SQLResponse` json unmarshaller.

Another significant change is the modification to the `ExprDataType` interface:
```
type ExprDataType interface {
	exprDataType()
	TypeName() string
	TypeDescription() string
	TypeInfo() map[string]interface{}
}
```
I added two more methods in order to distinguish between a type (`DECIMAL`), its
description (`DECIMAL(2)`), and its type info (`"scale": int64(2)`). Currently,
the description can be used as the field definition in a CREATE TABLE statement,
but we may want to re-think that. Also, Decimal is the only type currently using
TypeInfo.

Finally, I tried to consilidate things around `dax.FieldType` instead of
comparing against parser types outside of sql3. We still have some sql3 parser
and planner types lurking about, but we can address those in future commits.

* Add some test coverage

* smoke test expected INT, now int

* minor fixes

* Introduce WireQueryResponse and related types

This also changes dax.FieldType to dax.BaseType.

* Populate WireQueryResponse correctly

Currently this is in the http handler, and in the queryer.

* Convert sql3 and dax tests to expect pilosa.WireQueryField in results

* fix PQL tests in the SQL defs

* Address a few of the skipped sql tests in dax

(cherry picked from commit f4385df2cf)
2022-12-12 09:01:20 -08:00
tgruben
2fc7abd72e Dataframe (#2241)
* Dataframe

(cherry picked from commit 2f1beaf119)
2022-12-12 09:01:20 -08:00
pokeeffe-molecula
e2500fcee8 Fb 1767 (#2308)
* identifiers can now have the '-' character
* skip some integration tests

(cherry picked from commit cca319fbe5)
2022-12-12 09:01:20 -08:00
pokeeffe-molecula
b17ee6a203 fixed join bugs by fixing query optimizer (fb-1699, fb-1700) (#2307)
this commit changes the way the plan is retrieved; implements Stringer on types.PlanExpression in preparation for HAVING support; removes last vestiges internal float64 arithmetic; implements a filter on PlanOpFilter; fixes various bugs in the PlanOptimizer when rewriting qualified references

* fixed selects with unqualified identifiers

* handle bad and non-existent query param inputs more appropriately

* added test coverage for PlanExpression Stringer

Co-authored-by: Matthew Jaffee <jaffee@pilosa.com>
(cherry picked from commit 5f662d2bce)
2022-12-12 09:01:20 -08:00
Travis Turner
cd260fe665 Make sure table stub is valid as a pilosa.Index name (#2306)
We use part of the dax.TableName in the pilosa.Index.Name.
This just ensure that we don't let invalid characters get through.

(cherry picked from commit 10e8aa5c45)
2022-12-12 09:01:20 -08:00
Kord Campbell
b468da449f fixes FB-1768, two entries for the holder command
(cherry picked from commit 875c7492fe)
2022-12-12 09:01:20 -08:00
Seebs
f06b187bbd improve coverage in tests
(cherry picked from commit bd2b9ac225)
2022-12-12 09:01:20 -08:00
Seebs
f2d1c3f459 write header before body
This produces an http warning that doesn't make the test fail.

(cherry picked from commit d0ccf8e4e4)
2022-12-12 09:01:20 -08:00
Seebs
1009fa4164 actually honor provided stdin/stdout
the BackupTar and RestoreTar functionality was ignoring provided
readers, which doesn't matter for real usage but breaks tests
by making them dump raw tar binaries to stdout.

(cherry picked from commit 93153a97db)
2022-12-12 09:01:20 -08:00
Seebs
3c05f1ff37 Distinguish between usage errors and other errors
Cobra automatically displays usage messages, and also a gratuitous
"Error: [...]" line in some cases, when any error at all occurs
running a command. To suppress the usage message, you have to set
cmd.SilenceUsage to true. But the code that would do this doesn't
have access to it. To address this, we introduce a category of
"usage error", implemented with stdlib error wrapping (%w) and
use errors.Is to check for it. There's also utility functions
to do this checking automatically, or indeed, to handle wrapping
of the ctl.SomethingCommand and handle running it with a suitable
context and everything.

In fact, several of the places we're checking for usage errors,
we can never actually report one, but we're checking consistently
so that if we want to report usage errors, we can.

For instance, server.Start and (dax)server.Start don't ever
return usage errors, right now, but we're checking their responses
anyway.

(cherry picked from commit c681642734)
2022-12-12 09:01:20 -08:00
Garrison Davis
e426ec414a Add back ability to make on-demand instances
Also addressed the couple TODOs I left behind in the terraform.

(cherry picked from commit 4d75eda557)
2022-12-12 09:01:20 -08:00
Garrison Davis
f7a7a8a0c0 Make tflint and terraform fmt changes
(cherry picked from commit d028b6bc4b)
2022-12-12 09:01:20 -08:00
Garrison Davis
bc01e4c55c Make spot instance selection dynamic
The idea behind this is to give AWS more information about what
instances we can let it actually instantitate, rather than have it be
one fixed instance type.

e.g., in this case, we are okay with any Graviton instance with at least
2 vCPU and 8 GiB memory.

The easist way to do that is to instead use a launch template, with
ec2_fleets or spot fleets.

I took out the part where we even support on-demand instances. This can
be readded later if it is necessary.

(cherry picked from commit 3bfb65eb58)
2022-12-12 09:01:20 -08:00
Travis Turner
202a296131 rename shard partition field (#2302)
* Rename dax.Shard to dax.VersionedShard

* Rename dax.Partition to dax.VersionedPartition

* Rename dax.FieldVersion to dax.VersionedField

* Rename go files to a standard

(cherry picked from commit 49d0e0fbc8)
2022-12-12 09:01:20 -08:00
Matthew Jaffee
10026d0a90 run just the batch tests for the batch tests
(cherry picked from commit 12dd3c5996)
2022-12-12 09:01:20 -08:00
Seebs
490a2c57b0 stop uploading test results to sonarcloud, stop telling it we did
This gets complicated. For coverage output, sonarcloud supports wildcards.
For test output, it doesn't. So we weren't getting meaningful results,
just weird error messages. I fixed that, and got thousands of lines of
other error messages because it wasn't finding the test source files.
That looked like this:

	WARN: Failed to find test file for package
	github.com/molecula/featurebase/v3 and test
	TestTranslation_Primary

But we don't actually need the test reports sent to SonarCloud, because
"which parts of your test suite are being run" is sort of inherently
"basically all of them" with go test. So rather than continuing to do
that, we drop it.

Since we're dropping that, we don't need the JSON output from go test
anymore, so we drop that too, and the tee commands, and the "artifacts"
from the tee commands, and now our test output is human-readable and
slightly faster.

We also bump SonarCloud to 4.7.

We also fix the tests to use GOVERSION sometimes and GOFUTURE other
times, and bump from 1.19.2 to 1.19.3.

Also a couple of minor cleanup (adding explanatory comments,
combining adjacent grep commands, etc.)

(cherry picked from commit da4fb4ab4e)
2022-12-12 09:01:20 -08:00
Seebs
575f2c04a5 drop invalid UTF8 encoding from encoding tests
Testing unicode is great, but we appear to have had a couple
of cases where we were using strings that weren't valid UTF8.

Weirdly, other instances of these strings work -- I think because
they're in raw quotes (backticks) rather than strings. Anyway,
this is what SonarCloud fusses about.

(cherry picked from commit ab543adbe3)
2022-12-12 09:01:20 -08:00
Travis
04aa8b18fc Add DAX - full list of squashed commits below
In this commit, the Directive is mocked; it doesn't actually reach out
to a controller.

Limits key translation to only those partitions (per index) specified in
the Directive. Attempting to create or find a key (or ID) for a
partition which is not handled by this node will result in an error;
translation requests are no longer forwarded to other nodes.

Limits import into only those shards specified, per index, by the
Directive. Attempting to import into a shard which is not handled by
this node will result in an error.

Stub out /directive endpoint

The `applyDirective()` method still needs to be implemented.

Update mds references to use the new /mds/types structure

In mds, we moved the shared types to mds/types. FeatureBase needs to
reference those instead.

This also bumps the mds version in go.mod.

Implement the Add/Remove Index part of Holder.ApplyDirective()

This adds functionality to `Holder.ApplyDirective()` which adds or
removes indexes (tables) based on those provided in the Directive. Still
to be implemented here: shards and partitions.

WIP: remove client from Batch

Move Batch into its own package: batch

Also, in order to avoid import loops, this introduces packages:
/batch/types
/client/types

Reorganize the Importer-related code

Moved the Importer interface to package: batch
Move the "pilosa client" implementation of the Importer interface to
package: client

Modify batch.NewBatch to take an Importer (not client)

This commit modifies the batch.NewBatch() function to use a functinal
option on Batch to inject an Importer into the Batch. Prior to this,
NewBatch() took a pointer to a client, which was a little too
restrictive. Now, MDS can implement an Importer which uses information
from MDS to determine to which node(s) the import calls should be directed.

Add client.SetAuthToken() method to satisfy SchemaManager interface

Update ApplyDirective logic to include fields.

This needs more work, but it was enough to get a basic test passing.

Move Transaction type into /types package.

Add interface check on batch.Importer no-op implementation

Updated ApplyDirective to create all currently support Field types

There are still the following TODOs:
- [ ] impolement field options (ex: decimal scale, int min/max, etc).
- [ ] `time` fields

Added support for Decimal.Scale in ApplyDirective

Update mds dependency

Add /health endpoint

Update to use dax (dax/mds) instead of mds.

After moving the mds repository into the dax repository as a
sub-package, this commit changes everything in FeatureBase to use the
dax repo instead of the now abandoned mds repo.

Introduce and implment the WriteLogger interfaces.

This adds both a `WriteLogReader` and `WriteLogWriter` interface. They
are both implemented by the implementation: `fileWriteLogger`. The
`fileWriteLogger` uses the dax/writelogger API to append log messages to
files on disk.

Add WriteLogWriter.ImportRoaring method to interface

This commit adds the `ImportRoaring` method to the `WriteLogWriter`
interface. Still to implement are the `Import` and `ImportValue`
methods.

Reorganize the ApplyDirective code

The primary goal was to cache the incoming Directive on the Holder prior
to applying all of the changes in the directive (i.e. loading data from
the WriteLogger) because applying those changes often validated against
the accepted state of the node.

Implement all of the WriteLogger read/write methods

Implement the HTTP WriteLogger implementation

WIP: Introduce shard.Version. Implement snapshotter.

Add HTTP Snapshotter implementation

This also recofigures server to use the HTTPSnapshotter instead of the
FileSnapshotter.

Implement snapshotter: TableKeys

Implement snapshotter: FieldKeys

Dependency dance

last of the dependency dance

Add support for prototype

This adds the Makefile targets to build the docker container and push it
to ECR.

SQL3 changes which break with dax changes

Missed TODO: implement FieldVersion version to WriteLogger

Address bug causing missing TranslateStores to error

Originally, we tried to limit the TranslateStores which get allocated to
only those for which the node is responsible. This works when adding a
new table. But if a table already exists, there's no logic to start
missing TranslateStores.

This reverts back to the old FeatureBase logic which brutishly allocates
a TranslateStore for every partition, even if one is not needed.

We need to address this by allowing the ApplyDirective logic to
initialize TranslateStores when they don't yet exist.

Move the ImportRoaringShardRequest type to the types package

Since the ImportRoaringShardRequest object is part of the Importer
interface, we need to move it to a non-root (i.e. pilosa) package. All
the other interface types are either concrete types or part of a
sub-package (such as roaring). We do this to prevent an implementer of
the interface from having to import the entire pilosa package and risk
circular imports.

buncha changes to support latest dax stuff

Move dax related types to /dax sub-package

This commit moves all the common "dax" types into the /dax sub-package.
The idea is to ensure that featurebase does not import dax at all.
It's ok if dax imports featurebase.
In the future, we might need to split the dax sub-package (common data
types used by muliple molecula data-plan services) into it's own repo.

Add type: dax.Schema

This isn't currently being used; I started to use is as a replacement
for pilosa_client.Schema, but then deferred that. But we'll need to do
it eventually, so it doesn't hurt to have this type in place.

Export RowIDs.Merge() method for use in orchestrator.

Add CreateSQL method to dax.Table type

The CreateSQL() method will return the "CREATE TABLE" statement required
to create the dax.Table.

Comment out confusing writelogger log message.

We need to revisit this, but for now, this log message is confusing.

Also, rename daxSharder to versionStore.

Remove hard-coded AWS account

Implement more FieldOptions such as Epoch

Some of the FieldOption logic was stubbed out in the dax package. This
commit fills that out more; specifically, it adds the
dax.Field.Options.Epoch parameter.

export stuff needed for TopK in orchestrator

export ValCount stuff to implement Percentile in orchestrator

export more stuff to support less code in orchestrator, shared objs

Port dax repo over to featurebase/dax (run all as sub-services)

This commit does ALOT. Sorry.
It introduces a `featurebase dax` sub-command which can be configured to
run the various dax services as sub-services within the same process, or
individually as the lone service in process.

It also changes all the URL paths to be prefixed with the service name.
So for example, instead of calling localhost:8080/status, you would now
call localhost:8080/featurebase/status.

Also, note that all services provide a /health endpoint to confirm they
are running in process.

Clean up integration tests. Remove PILOSA_ config prefix.

Remove duplicate clients (mistake from porting dax to featurebase)

Rename sub-service "featurebase" to "computer"

In the places where we have hard-coded the sub-service name into a URI
path, I've tried to tag the line with a comment containing:
`// #SERVICEPATHPREFIX`

Update copilot manifest files to reference "computer"

Port dax/README.md from dax repository

Separate (toml) Queryer Config from Injections

We needed to separate the toml config from the configuration required to
inject sub-services into the Queryer. I'm not sure this is the best
solution, but it's *a* solution. So here we are.

Clean up (i.e. remove) the queryer "implementations" package

Remove old test file

Run WriteLogger and Snapshotter as local sub-services.

Prior to this commit, the writelogger and snapshotter services only
worked when run as separate services. This allows them to be run in the
same process as all the other dax services.

There is still some naming issues that we should address, but it's
functional for now.

Clean up (i.e. organize) the intra-service interfaces.

Implement alpha Director for local messages from MDS to Computer

Prior to this commit, messages from MDS to the computer service were
still going over http. This commit introduces an interface
implementation which registers the local computer command, and use that
command's API to directly reference methods used by the Director.

Clean up a few more interface names

Add Queryer OpenAPI document.

Update copilot manifests to reflect latest changes

Add OpenAPI documents for WriteLogger and Snapshotter

Add OpenAPI document for MDS service

Add OpenAPI document for Computer service

Consolidate errors to use fb/errors package.

This commit is a first pass at trying to ensure that all of the DAX code
uses:
"github.com/molecula/featurebase/v3/errors"

This package is a wrapper for "github.com/pkg/errors", so going forward
we want to avoid importing that package.

The only method which isn't backward-compatible is `New()`; the
New() method in the featurebase/errors package takes an errors.Code. If
this becomes a problem, we could change this by reverting New() and then
introducing something like NewCoded(). But for now I think it might
actually discourage someone from just creating a New() error without
thinking about how it should be coded.

Introduce VersionStore interface

Move the existing VersionStore code to the `inmem` package as the
in-memory implementation of the new dax.VersionStore interface.

Introduce NodeService interface

With this, the Controller can maintain a registry of nodes by using this
NodeService interface as opposed to an in-memory map of nodes on the
Controller struct.

This also adds an inmem implementation of the NodeService interface.

Introduce controller.Balancer interface

This moves the existing balancer package to controller/naive package.
The idea is to allow us to add a different Balancer implementation in
the future.

Introduce DirectiveVersion interface

This commit also includes *A LOT* of refactoring to use dax.Worker and
dax.Job types everywhere instead of strings.

Introduce Schemar interface

The previous `Schemar` struct was moved to the `schemar/inmem` package,
and `Schemar` is now an interface implemented by that inmem package.

Remove unused type `nUnit`

Add boltdb implementation of VersionStore interface.

This removed the previous sqlite implementation; we decided not to use
sqlite for now (as a basic, local disk implementation) because it
requires CGO.

--------------------------------------------
No longer applicable:

Add sqlite implementation of VersionStore interface.

This commit implements the VersionStore interface using sqlite. Sqlite
requires CGO, so this may not be something we want to include, but it's
implemented here to get a feel for how an external implementation might
be used; the next step will be to determine how the user configured
FeatureBase to run using sqlite as a backing store for services like
MDS.

Add boltdb implementation of NodeService and DirectiveVersion interfaces.

Add boltdb implementation of naive Balancer interfaces.

This includes the two interfaces defined in `naive/balancer.go`:
- WorkerJobService
- FreeJobService

Add boltdb implementation of Schemar interface.

clean up a linter issue

Thread context.Context through all the interfaces.

Some of the interface implementations are going to use context, so we
need to make that part of the interface. The boltdb implementations, for
example, take a context. This is probably so we can do things like
cancel or timeout operations.

Update interfaces to return error; remove `panic(err)` everywhere.

Down-rev grpc version to 1.38.0

Later versions (after 1.42.0?) cause MustRunCluster.Close() in tests to
deadlock.

This commit also adds an `isComputeNode` feature flag around some of the
write log and shard/partition check functionality so that it doesn't run
under normal conditions (this is excercised by running the sql3 tests
for example).

Add MDS_Persistence test to cover meta data persistence

This adds a basic test which configures the MDS container to use boltdb
as its persistence storage, saved on a docker volume. Then, the mds
container is stopped/replaced, and we confirm that the data stored on
the volume is availble to the new MDS container.

Fix a few things after rebase with sql-experiment branch

The lastest version of sql-experiment contains a fairly significan
refactor of the way query iteration works. This commit adjusts for those
changes.

pull dax IDK changes in to FB IDK (#2177)

* pull dax IDK changes in to FB IDK

* Move docker-related IDK build stuff to featurebase root

Building the docker image required the root level go.mod and vendor
directory. This change moves the make targets to the root level
Makefile, and the Dockerfiles now copy the root level vendor directory
(and everything else in the root for that matter).

* Fix batch- and client-related tests

* InitializePoller on MDS restart/replacement

Prior to this change, if MDS was restarted, its internal poller (which
maintains an in-memory list of nodes to poll) is empty. This is bad,
because it doesn't know about nodes that it should be polling.

This change fixes that. Upon MDS startup, it intializes the poller with
the list of nodes that MDS keeps in persistent storage (currently:
boltdb).

* Add EFS volume to MDS Copilot manifest

This allows us to use MDS's persistent storage (via boltdb) in the
Copilot demo by saving metadata in a boltdb file on EFS.

* Thread logger.Logger through all dax components

* Revert some of the breaking changes from DAX development.

When we first started prototyping DAX, we made changes to the
featurebase core code which would have broken the existing featurebase
functionality. This commit reverts some of those changes. Anywhere that
we need to modify core featurebase functionilty, we put it behind some
kind of feature flag. This flag is typically determined by whether the
running node is a "compute" node (i.e. DAX.COMPUTER.RUN = true).

Co-authored-by: Travis Turner <travis@molecula.com>

add packaging for DAX

need cgo for datagen build

bind to 0.0.0.0, pass GOOS and GOARCH explicitly

not sure if the explicit GOOS/GOARCH is actually necessary...

Get INSERT INTO (aka ingest) working through SQL3

This commit does a few things which I'll try do describe here.

- Introduces a Qctx interface. The existing Qcx is an implementation of
  this interface, and can be used exactly how it has been. But this
  allows us to abstract away the notion of Qcx in the Queryer (which is
  handling SQL3) until we're ready to address that. As an example, the
  Qcx has a notion of a featurebase Holder, but that doesn't make sense
  when we're at the Queryer layer. For now, the Qctx used in the Queryer
  is a no-op.

- Adds a ComputeAPI interface implementation for the Queryer. This is
  effectively the Import() and ImportValues() methods used for ingest.
  The logic here handles the incoming ImportRequest by first doing any
  necessary column and row translation for the entire request, then it
  splits the records by shard, and generates a new ImportRequest per
  shard with only the shard-appropriate records.

- Changes the mds.Importer to take an MDS interface implementation
  (which can be an mds client) instead of an mdsAddress. This allows us
  to use a localy MDS implementation rather than assuming we need a
  client to make calls over a network.

Add queryer.Importer interface to handle ingest via SQL (#2203)

* Add queryer.Importer interface to handle ingest via SQL

This is meant to support ingest through SQL when the queryer and the
compute services are running in the same process, or when they are on
seperate processes and need to talk via http client.

* remove datagen from RPM

was originally added as a convenience to generate test data, but is
unused and annoying because datagen doesn't easily cross-compile due
to cgo

* add marshalUnmarshal to controller to avoid passing pointers

passing pointers across API boundaries can cause unpredictable things
in local vs remote configurations.

Co-authored-by: Matthew Jaffee <jaffee@pilosa.com>

"fix" a few issues with wrong default partition numbers

these still need to be properly fixed and actually get the correct
data from MDS

go mod tidy

Introduce TableQualifier (OrganizationID/DatabaseID) (#2220)

* add check in ApplyDirective that version is increasing

fix TestAPIDirective to make version always increasing

* fix docker image build and break out dax test in CI

We have to run the DAX integration tests separately as they call out
to Docker, and so it isn't easy to run them in a Docker container as
the other tests do. So we run them directly on the CI runner which has
Docker and Go installed.

We also explicitly exclude these tests from running during the other
tests.

Also my editor was automatically reformatting some comments badly
which is why I added the "data" thing in those two places

* add timeout to poller

* give Poller a default Logger

apparently we can NPE sometimes, seen in CI: https://gitlab.com/molecula/featurebase/-/jobs/3028286364

* bunch of testing fixes, mostly IDK/DAX related

make MDS error if sendDirectives errors, don't just
log. sendDirectives can error if computer nodes disagree about the
validity of a schema (for example), in which case it might need to get
deleted and user notified somehow. very messy, needs more thought.

re-introduce old env prefix to maintain compatibility with master
branch

make self-contained dax container for IDK testing

build IDK images from source (now that all the source is available
since it's in the same repo)

catch errors in DoExtractQuery in idktest.go

fix IDK bug where prefix path was hardcoded in all cases rather than
only when useMDS was true

fix TestBatchTargetMDS... needed to add field options and catch error
when creating table. also needed an _id field

* fix env prefix in tests

* WIP getting tests to pass, wanna see CI

* don't error if we get a zero version directive and we don't have a

directive yet

* cleanup debugging junk

* "fix" future.rename thing, run IDK tests

* Introduce TableQualifier (OrganizationID/DatabaseID)

This commit introduces a lot of new types (in dax/table.go) related to
TableQualifer (which is made up of OrganizationID and DatabaseID), as
well as things like TableID and TableKey.

For the most part, we try to thread a QualifiedTableID through the
entirety of DAX. There are some places (for example in the Balancers,
which are just aware of string keys) which use a string TableKey
(tbl__org__db__tableid).

* Remove some debugging comments

* Add Org/DB support to CLI.

This commit adds support for special commands:

SET
SET ORG acme
SET DB db1
USE db1

* remove ".pulled" from IDK Makefile

I don't think we need it any more as most things can be built
locally. I think it was only there to refresh the FeatureBase images
that were tagged as master, but we don't need to do that any more.

* Change DAX json tags to kebab-case (i.e. hyphenated)

This commit also renames some struct arguments to more accurately
reflect their type: for example, renaming `Table` to `TableKey` when the
type is TableKey.

* Return DAX TableName in SHOW TABLES (instead of Index.Name)

There are cases where SchemaAPI is used to return DAX friendly table
names (as opposed to featurebase index names, which are DAX TableKey).

This is an attempt to do that. With that said, it's not ideal because
anything could call those API methods and expect the other type.

* Fix a bug which wasn't completely dropping a table.

When using boltdb as a backend, DROP TABLE wasn't removing the
reverse-lookup key for the table in boltdb.

* Remove idk/testenv/certs which got accidentally committed.

also update .gitignore to include those.

* Fix IDK ingest tests to be TableQualifier aware.

* Add example Table types to dax/table.com godoc.

* ignore idk.Main fields for flags, upgrade commandeer

* go mod tidy

* Fix DAX integration tests: ingester using wrong ENV VARs

We change from ORGANIZATION_ID to ORG_ID
and from DATABASE_ID to DB_ID

* Clarify things around idk (docker) tests

* Stop running TestKafkaSourceIntegration with t.Parallel()

This test can't be run in parallel as it's currently written. Doing so
allows for interleaving of messages to the same kafka topic between
tests.

I didn't attempt to modify the test so it could be run in parallel. That
could be done, but left for someone more ambitious.

Co-authored-by: Matthew Jaffee <jaffee@pilosa.com>

Require Directive.Version be a non-zero value. (#2227)

Because the directive cached on the holder is not a pointer, its default
version is 0. In order to avoid having to compare against that, we just
require that Directive.Version start at 1.

General, non-invasive code cleanup and comment adjustment.

Move ImportRoaringShardRequest out of the types package

Early on in the DAX development, I moved ImportRoaringShardRequest into
a types package. There must have been some import loop going on, but
since that is not longer the case, it's safe to move this back into the
core featurebase (er... pilosa) package.

Move Transaction struct back into the pilosa package (from types)

Revert some name changes (cli -> client)

Add DAX Handler CloseTimeout

This was implemented in htt_handler.go, but it had been commented out in
the DAX handler. This just uncomments that and finishes the
implementation.

Remove Qcx from queryer.Importer interface

This sets us up to revert the Qctx interface that was initially
introduced to allow us to abstract away the need for a Qcx when calling
the ComputeAPI from a remote service (i.e. the queryer).

Add some go-doc comments and remove unused code.

Move SchemaManager setup from datagen to idk.Main (#2233)

The set for idk.SchemaManager (for dax implementations) was previously
in datagen. This may have been because of some import loop problem
during development, but that's no longer an issue.

The setup for this should be in idk.Main so anything using that can
leverage the MDS-specific SchemaManager setup.

Fix issues around nil TxFactory

First, don't return a nil. Rather return a new *TxFactory (with no
holder).

Second, don't call `f.holder` in the testhook outside of checking if
`f.holder` is nil.

Wrap all bare errors

Make service prefixes constants

Instead of having `"computer"` throughout the code, use instead a
constant: `dax.ServicePrefixComputer`.

MDS skip errors when sending empty directives

also add in the docker-login and ecr-push changes for serverless DAX

Fix the logic in Directive.IsEmpty() (#2236)

Update the cached value for Index.translatePartitions

In the case where a node already knows about an index, but its
assignment of partitions for that index changes (for example, when
another node goes down and the node in question is now responsible for
more partitions than it previously was), then we need to update the
cached value of Index.translatePartitions because that's used in
translation checks.

minor fixes for IDK-related bugs

WIP: tokenize CLI to access cloud

FB CLI cloud support with automatic token refresh

Also adds support for a GET command which allows making HTTP GET
queries to cloud CP which can be handy for debugging stuff. E.g. GET /v2/databases

buncha little fixes working on writelogger stuff

fix writelogger/snapshotter setup bugs

implement writelogging for importRoaringShard

add debug endpoint to MDS

use shard transactional endpoint in MDS datagen

add debugging to API related to writelogger

revert handleroption change

clean up big PR

remove "GET" command from CLI for making arbitrary HTTP request to
cloud control plane (was a messy hack and not that useful)

remove json tags from FB objects where we had to duplicate the object
elsewhere due to import loops and weren't actually json encoding it

unexport handlerOption which was exported to try to avoid doing
certain things if we're in DAX mode, but I didn't end up merging that code.

remove (hopefully) unecessary extra call to api.indexField

fix some formatting, unexport some vars, godoc, etc

oops, fix build failure

Update FeatureBase CLI to support a standard deployment

The standard deployment uses a different endpoint and request payload.
This commit tries to detect is the standard deployment is being used,
and if so, it uses a standard-specific FBQueryer.

It also modifies the auto-detection logic to try standard featurebase
and dax ports in the case where a port was not provided.

MDS API refactor (#2259)

* MDS API refactor

table IDs are exposed but only created server side

also cleaned up dax Makefile

* clean up review feedback

Co-authored-by: Travis Turner <travis@pilosa.com>

* remove TablesByName

* rip out inmem implementations and use boltdb everywhere

* remove inmem balancer, create bolt tempfile by default on startup

* WIP on snapshot table impl and test

* Minor comment and code layout adjustments.

This commit also adds the `Equals` method to `QualifiedTableID` for
equality comparisons. It's no longer safe to compare struct (two structs
might still be equal even if one of the structs doesn't have a `Name`
value.

* Use a unique docker network for each dax test

Ocassionally we would see some test failures due to a network already
existing. This shouldn't happen, but to avoid that, this commit
generates a unique name for each sub test (which gets deleted at the end
of every test).

* Fix one instance of NewQualifiedTableID losing Name

We should probably check the other instances and see if Name is getting
lost.

* simplify unique network stuff and fix api directive tests

* Remove TableIDRequest and TableIDResponse types for /table-id (#2267)

For the mds/table-id http requests, just use dax.QualifiedTableID as
both the request and response types.

* remove lattice from dax, no error on node re-reg, dax docker-compose

* various updates

* WIP: mds-refactor branch review

* no-op on SnapshotTableKeys if table is not keyed

* Makefile helpers

* add doWeCare so controller doesn't fail unnecessarily

* clean up table creation (#2272)

* Strip underscores from TableID stub name

* fix boltdb versionstore tests: generate unique, sorted tables

* fix controller test related to reregistering a node

* JobSet -> generic Set

Co-authored-by: Travis Turner <travis@pilosa.com>
Co-authored-by: Travis Turner <travis@molecula.com>

Cleanup after rebase on master

The latest rebase on master entailed all the client/batch changes as
well as some of the qcx refactoring. It made for a hairy rebase. This
commit fixes some of the tests that were failing after that rebase.

Fix batch/client import loop missed during rebase (#2280)

It's not surprising that `batch` can't import `client`. It was doing
that here (importing an error type from the `client` package). What is
surprising is that it's okay for `batch_test.go` to import `client` even
though `batch_test.go` is an internal test and therefore part of the
`batch` package.

different boltDB's for schemar/controller, explicit balancers

nice helpers for dax docker-compose, make build really fast

build FB binary outside of docker, then create Docker image with its
working dir in an empty subdirectory so it doesn't send a GB of
context to the daemon.

error on unassigned jobs and use client with timeout

fix CR feedback

deregister batch of nodes

also make removal faster via director dial timeout

implement WorkersForJobPrefix so orchestrator doesn't make up shards

also fix some godocs and remove unused method

Run sub-tasks of a Directive concurrently in a worker pool. (#2275)

* Run sub-tasks of a Directive concurrently in a worker pool.

This allows the compute node to concurrently load shapshot and writelog
data concurrently, instead of one keyset/partition/shard at a time.

It introduces a config parameter called `DirectiveWorkerPoolSize`.

* code review cleanup

* Use unique container names in DAX integration tests

We were seeing "container already exists" errors in CI, so just to be
safe, this commit constructs a unique container name for every container
in the DAX integration test run.

Stub in SystemAPI to Queryer (note: will not work if used)

This just makes is so that dax can compile. Actually implementing
system-table functionality for dax will take some planning.

Tlt/dax merge prep (#2282)

* Remove copilot directory

* Remove Dockerfile-datagen-long

* Remove orphaned RegisterNodeRequest

This type is not defined in the dax/mds/http package.

* implement TIMEQUANTUM and TTL in Table.Field type

* Remove the "service" misdirection in queryer/writelogger/snapshotter.

We had originally used an additional layer, er.. package, for a "service".
The main distinction was that the Config differed in that it was
internal, unlike the Config that we need to provide for the top-level
server config (i.e. toml). Having that additional layer just to support
a different Config seemed premature at best. So I'm removing it.

* Remove dax docker containers no longer used in tests

Since we run everything as "featurebase", we don't have multiple
container types anymore.

* Some minor comment updates

* Remove nfpm stuff related to dax

* Fix linter issues

Fix "duplicate" issues raised by sonarcloud.

run docker components of dax integration tests with coverage

trying to get dax integration coverage

add coverate volume mounts throughout dax integration tests

add a lock, tweak dax Makefile, remote flag on query handler

remove some unused code

convert batch tests to use clustertests to get coverage

maybe fix clustertests

more authclustertests fixes, test is failing locally

but also seems to have been silently failing in CI prior to these
changes... let's see if it's still silent

fix some lint to kick CI

just re-running the job wasn't working... strange behavior

remove RetryLogic test and pipe which don't work

RetryLogic test removed due to etcd changes. Seebs thinks we shouldn't
test this here.

Pipe was being ignored since we're no longer using "bash -c" to
execute the command. If we need to generate that output file we'll
either have to reintroduce bash -c and set -o pipefail so that it
actually fails properly, or figure out some other solution.

shooting into the dark...

first cut at bulk node registration

remove unused stuff from batch tests, set coverpkg to ../...

batch registration timeout and fix tests

disable most tests and don't run fb background batch test

debuggin!!!!!!!!!

and then he tried this....

Implement importer (for INSERT INTO) in the Queryer

Prior to this, we we passing a nil value in for the importer to the
planner.NewExecutionPlanner in the Queryer. This meant that INSERT INTO
statements didn't work. Now they should.

It uses the importer that we build for IDK in /idk/mds/importer.go, and
wrapps that with a type that can determine if the provided string
"index" is of the form indexName or TableKey.

turn off debug mode, fix log saving

Run sql3 test definitions in a dax integration test

There are currently 22 tests which are not passing. They are skipped in
the "skips" slice.

WIP, not working, pql queries to tests

Add TableQualifier to PQL query logic in the Queryer

Add more PQL tests to the keyed table

Allow instant node registration if registration-batch-timeout=0

When running dax services in process, we don't want to wait 3s for the
compute node to register; we know it's there because it's in the same
process.

Fixes related to IncludesColumn PQL test.

Tests for ConstRow and FieldValue

cleanup

add UnionRows and Options, better error reporting on bad queries

delete unused schemar client.go, clean up unused in batch test CI

move test timeouts into more reasonable territory

apparently this had already been done, but got merge-stommped at some point

move dax bolt test helpers into dax package

Add computer CheckIn routine (#2296)

* Add computer CheckIn routine

This adds a background routine which sends a "check-in" request to MDS
every <interval>. This is to address the case where the poller has
removed a computer node from the node list (due to a network fault, for
example), but the node is still healthy and becomes available again. In
that case, the node needs to "check-in" to tell MDS it is still there.
MDS will likely send the node a new directive with Method=reset telling
the node to delete all of its data an apply the latest directive.

* Don't send directives to Deregistered (i.e. removed) nodes

We have an issue where we're locking on sendDirective in the
controller, and when the node is unavailable, the send hangs and never
releases the lock. This is a temporary fix for that until we address the
real problem.

Fix .gitlab-ci.yml after rebase

fix some indentation shenanigans

(cherry picked from commit 20a8b5713a)
2022-12-12 09:01:20 -08:00
Bruce Baranowski
06a63021b1 Re-allowed concurrency for molecula-consumer-csv when not using '--auto-generate' (#2294)
Concurrency was previously fully disabled due to duplication when using '--auto-generate' but testing has shown that it works correctly when not using that flag. Added the required check and updated help text

(cherry picked from commit ba70bee875)
2022-12-12 09:01:20 -08:00
Seebs
c99faa1793 bump timeout on overlapping write requests test
At 50ms, we see sporadic failures in CI. So much for "this should
only need a couple milliseconds". Bumped timeout to avoid that.

The challenge here is that we have some tests which *want* to hit
the timeout to confirm that we aren't allowing things we shouldn't.
But we don't want the test to hang forever. But we want to be sure
it is actually stuck and not just being slow...

(cherry picked from commit f1a68f8a82)
2022-12-12 09:01:20 -08:00
Fletcher Haynes
2709973b08 Fixed import in idk ingest 2022-11-17 15:56:14 -08:00
Fletcher Haynes
8e03df719d Fixed a few more conflicts around imports with the latest sync 2022-11-17 10:00:53 -08:00
Fletcher Haynes
45e405b980 Fixed some import issues resulting from syncing the private repo 2022-11-17 08:58:22 -08:00
pokeeffe-molecula
e14cde7341 SQL BULK INSERT (fb-1749) (#2291)
SQL BULK INSERT

This change is to support a BULK INSERT/REPLACE statement that adds the ability to 1) take its input from a file, url or in-line blob 2) to map from the input source to the target columns
3) to transform data (using sql expressions) before inserting
4) support csv and ndjson formats

* improving test coverage

* increase test coverage again

* refactoring for handling transformation with types other than id and int

(cherry picked from commit 8f660a5033)
2022-11-15 11:33:38 -08:00
Travis Turner
53170cef64 Move SQL3 test definitions into sql3/test/defs package (#2289)
This so other packages can import and use them in their own tests.

(cherry picked from commit eec278893a)
2022-11-15 11:33:35 -08:00
Julio Martinez
b3a408aa4e Packaging improvements to improve mcloud updates (#2288)
* Update package scripts so they restart services based on the operation (update vs install)

Co-authored-by: Julio Martinez <julio.martinez@logicmonitor.com>
(cherry picked from commit e25e395bab)
2022-11-15 11:33:12 -08:00
Seebs
ec6f256df9 rename protobuf interface
For unrelated reasons, we renamed the protobuf interface from
package "pilosa" to package "proto". This means our GRPC endpoints
need to live under "proto.Pilosa" instead of "pilosa.Pilosa". This
means lattice needs to be configured the same way.

(cherry picked from commit 3a8d08fed5)
2022-11-15 11:33:12 -08:00
pokeeffe-molecula
eb842274ec fb-1744 implement system tables (#2276)
* implement system tables that contain internal state information from FeatureBase

* review feedback

* review feedback

* removed sys prefixes

(cherry picked from commit 45067b4e32)
2022-11-15 11:33:12 -08:00
tgruben
dcdc99db25 enable staticcheck in ci; remove dead code (#2281)
(cherry picked from commit 0d6a92aeaf)
2022-11-15 11:33:12 -08:00
tgruben
751b7a74fe staticcheck fixes (#2278)
(cherry picked from commit 0aa5efcc51)
2022-11-15 11:33:10 -08:00
Garrison Davis
13a32409f3 Fix server.GetIndexes userInfo logic
userInfo at the relevant line can be nil here. We check later if the
userInfo != nil (and assure it passes authn/authz) or if userInfo == nil
then we return all the indexes we can find.

(cherry picked from commit 407d52baa0)
2022-11-15 11:32:03 -08:00
Garrison Davis
ab2b48da0d Stop using string keys in contexts
This fixes the OriginalIP and RequestUserID in the main featurebase
package, and the Access and Refresh tokens, the UserInfo, and the
[]string of Indexes passed with context.Context(s) in the authn package.

An empty struct was used for all of these keys (and relevant helper
functions we added) to avoid allocations where possible while still
using the context functionality.

Some of the logic in the server.GetIndexes function was fixed.

(cherry picked from commit 0f5a56c958)
2022-11-15 11:32:03 -08:00
Bruce Baranowski
202501f7bf Makefile Cleanup
A cleanup pass to remove unused or broken makefile commands, this will help make the Makefile more clear and useful.
Removed unused makefile entries:
- release-build
- test-release-build
- check-clean
- release
- release-sans-ui
- plg
- install-bench
- generate-stringer
- pilosa-keydump
- pilosa-chk
- pilosa-fsck
- docker-test
- topt
- topt-race
- gometalinter
- install-stringer
- install-protoc-gen-go
- install-gometalinter

(cherry picked from commit 6800e52a39)
2022-11-15 11:32:03 -08:00
Seebs
7db504e5ee finish removing traces of ingest API
A couple of helper functions and types were left over from
the ingest API. Thanks, staticcheck!

(cherry picked from commit 0c904b3fae)
2022-11-15 11:32:03 -08:00
Seebs
10edb62b83 initial implementation of RBF backend
This provides us with most of the existing Tx interface, split
across QueryRead and QueryWrite. The functions not included here
are the ones that are used *only* for anti-entropy (ForEach
and ForEachRange).

We add additional testing to verify that TxStores are getting
closed correctly, to go with cleaning up the test directories they're
made in.

We also introduce some test wrappers that can automatically
fail tests on error, so tests don't need to be full of error
checks.

Also, now that I'm starting to think more about the flow of
writing tests using QueryScope, we add the missing "full
database" scope option, and make the Add methods return
their operand so (1) you can chain them, (2) you can use
the AddIndex(...) inline in a NewWriteQueryContext.

Also addressed a plausible performance concern in shardList,
and some comments that were stale or incorrect.

The test coverage here is skimpy on the actual RBF-calling
functions because those are trivial. We do, however, significantly
expand coverage in the random write requests, which are now
a mix of random writes and random reads, and add test cases
that at least hit a lot of the error checks once.

The Error() method is changed to be like (testing.T).Error(),
taking ...interface{} and using fmt.Sprint on them.

There's also some minor tweaks such as making the visualizations
more consistent, testing visualization generation on two kinds
of keysplitter, and so on.

(cherry picked from commit 7b434cd11c)
2022-11-15 11:32:03 -08:00
Seebs
c02a4b5e47 use Fatalf instead of Fatal(Sprintf)
(cherry picked from commit b4eb1b8bdc)
2022-11-15 11:31:41 -08:00
Seebs
a508a15f3b drop unused CreateDirIfNotExist function
I was wondering why this is exported, and the answer is, if it
weren't exported, staticcheck would have reported that it was unused,
which it is. We don't need a wrapper on os.MkdirAll that we never
use.

(cherry picked from commit e9a40cdc35)
2022-11-15 11:31:13 -08:00
Seebs
7d1fde7438 drop anti-entropy feature, since it doesn't work
The anti-entropy feature has never actually worked. We've been
talking about removing it or replacing it for ages, but haven't
had a concrete motivation.

But the anti-entropy interface is the sole user of several components
of the Tx interface, and now that we're trying to replace that
interface, being able to drop those components has some appeal, so
let's remove the one thing that used them, in the hopes that this
will simplify life.

This also lets us drop ForEach and ForEachRange, which were
barely used at all. The one surviving usage (CSV export) can be
handled by using the container iterator we already have, and
making ContainerCallback exported so we can use it to just call
things for every bit.

(cherry picked from commit fff9ddc1f5)
2022-11-15 11:31:13 -08:00
Jacob Brinlee
bf00d028c7 updating topic/partition/offset order (#2250)
* updating order of topic/partition/offset in log message

(cherry picked from commit dd30168b1c)
2022-11-15 11:31:13 -08:00
Jacob Brinlee
d2a4ca3168 SUP-286 - update lattice config (#2273)
* comment out configs

(cherry picked from commit ca16e7f8fa)
2022-11-15 11:31:13 -08:00
pokeeffe-molecula
ae3910a22b Test expression eval for tuple values in inserts (fb-1555) (#2270)
refactored comparison, equality and arithmetic expr eval for decimal data types and added a test to cover expression eval for inserts

fixed failing test

(cherry picked from commit 193ef7cba1)
2022-11-15 11:31:13 -08:00
Seebs
bf00561caa Drop the ingest subpackage and related endpoints.
The internal/ingest and internal/schema endpoints were developed with
intent that they'd be the primary interface new users would work with,
because they were Easy To Use, and did not require any kind of setup,
the counterpoint being that ingest done this way had performance issues
because it ended up with huge amounts of JSON parsing to reformat
things into our native format. But this was understood to be the price
of providing a new-user-friendly JSON ingest experience.

A year later, we have no evidence that it's ever been used. We never
even moved it out of the `/internal` path. It's a lot of very complex
fiddly code and we don't seem to be using it, and at this point, our
anticipation is that if we really need something, we'll use CSV, which
we already have working, or something in the new SQL code. Either way,
we don't seem to be using this.

(cherry picked from commit 16ccbc461a)
2022-11-15 11:31:10 -08:00
Seebs
cb252a0e82 stop ignoring degraded/down states
We think etcd's tendency to mistakenly mark nodes down may have
been addressed. We can't find out without checking for it.

The exact pool of methods in methodsDegraded may have bitrotted
some; for instance, it didn't have PastQueries or PartitionNodes
in it, but it looks like it reasonably should.

We rework the Replica1/Replica2 server tests to reflect the
intended semantics again.

(cherry picked from commit 164dec1703)
2022-11-15 11:25:39 -08:00
Seebs
4467c18baa drop Starting cluster state
The special case of Starting allowed us to make sure every node in a
cluster waited for the whole cluster to come up, but caused problems
later if a node died and came back. We drop the Starting state for
clusters, treating a STARTING node as equivalent to an UNKNOWN (or
DOWN) node for purposes of cluster state, so clusters will go from
Down to Degraded to Normal as nodes come up. We now wait for the
Normal state during initial bringup. We would previously have accepted
Degraded, if you could reach it, for instance if a node came up and
then went down again before another node finished starting, but I'm
pretty sure that was unintentional.

This solves a problem where while a node was down, we'd accept
queries that we could handle in a degraded state, but then we'd
*stop* accepting them when the node started coming back up.

(cherry picked from commit 52d3329434)
2022-11-15 11:25:39 -08:00
tgruben
95287c2ee2 add flag to bypass space check (#2265)
(cherry picked from commit f987009406)
2022-11-15 11:25:39 -08:00
Travis Turner
bcb32addaf Batch insert via SQL (multiple tuples) (#2243)
* Formatting adjustments made during code review.

While reviewing the BULK INSERT logic (in order to decide how best to
approach "ingest via sql" in the cloud), I made a few formatting and
comment changes. I'm just adding them here as a separate commit so they
don't muddy up my actual work.

* Parser modifications to support mulitple tuples in INSERT INTO

This commit doesn't include all of the changes required in the
planner. Fow now, the planner is simply modified to continue supporting
a single tuple (the first tuple in the list).

* Update the planner to handle multiple INSERT INTO tuples

This is part 1. It's still using the existing logic which builds an
ImportRequest for every record (and every field!).

The next step will involve using a client.Batch to handle the records.

* Introduce client.Importer interface (used by client.Batch)

Instead of the Batch having a pointer to a client, this puts an
interface there instead (which the client implements). It also allows us
to inject a different importer (i.e. other than a featurebase.client)
into the Batch.

* Decouple batch from client

This commit pulls batch-specific code out of the client package and into
a new batch package. It introduces the batch.Importer interface, the
methods of which replace all the calls that batch was previously making
directly to client methods.

Finally, it contains two implementations of the batch.Importer
interface: one is a wrapper around client, and the other is a wrapper
around featurebase.API.

* Use docker (instead of MustRunCluster) for internal batch tests

Because the `batch` package tests are internal, using
test.MustRunCluster() resulted in an import loop (because it eventually
imports `server`, and we can't have that). So this commit replaces the
use of `test.MustRunCluster()` with docker. The setup is basically the
same as that used in the idk docker tests.

Here we also remove all client-side references to `UseIngestAPI`, which
is an experimental (json) ingest api. It's still suppored on the server,
but here we remove the external usage of it.

* cherry-pick fix

* Use batch.Import() for sql3 INSERT INTO statements

* Thread logger into sql3

* fix batch test

* Fix some shadowing complaint by linter

* Address some test issues related to stringsets

* Exclude batch integration tests from CI

* Address PR feedback

- Added description to batch.README
- Consolidated grep commands in .gitlab-ci.yml
- Removed some debugging comments
- Replaces some inadvertantly removed license headers

* Add batch package to gitlab CI

* Updated CI for batch package

Updated CI include path

Update gitlab ci

Update CI

Update CI

Trying new include path for ci

Updated gitlab ci include path

Made idk race job optional for sonarcloud upload

add testdata directory

remove testenv from dockercompose file

use GIT_STRATEGY clone in batch CI

add testdata volume to dockercompose

Co-authored-by: Fletcher Haynes <fletcher.haynes@generalassemb.ly>
(cherry picked from commit 00ef2380e5)
2022-11-15 11:25:36 -08:00
Seebs
0c94a53a73 initial implementation of QueryContext design
This is living in a subdirectory for now so we can have better
turnaround time on tests and not have to build everything else
along with it.

This covers the logic that we can have *without* actually using
databases or the filesystem in any way, just to provide a framework
that lets us validate the logic handling overlapping queries.

The overall purpose of this is to prevent deadlocks, by ensuring
that database locks are only taken when we have already proven
that they are available. In short, the QueryContext preregisters
its "scope" -- the set of things it may want to lock. The operation
of creating the QueryContext can block, but it blocks with no
database locks held. Once it is unblocked, the scope it has reported
is now considered unavailable, and no other QueryContext using any
overlapping scope can complete creation until this QueryContext
completes. While it's running, the QueryContext can't request write
access to anything outside its scope. Thus, once created, a
QueryContext can always proceed, without being blocked, until it's
done.

Note that this does not fully address multi-node behaviors;
once you have a QueryContext blocking things, you need to not
make queries to other nodes that could be blocked in turn by those
nodes. In short, no write queries to other nodes while holding a
write-type QueryContext on the local node, because if two nodes
do that to each other at once, they can both be blocked.

We believe RBF is currently designed such that read-only accesses
don't block progress on writes, so non-write access doesn't
create problems.

We also have some code to allow us to create dot-format output
from the components of this system, which is mostly intended to
be a debugging tool.

(cherry picked from commit e3d137f29c)
2022-11-15 11:22:17 -08:00
Stephanie Yang
76d62418d7 update molecula references to featurebase (#2262) 2022-11-15 11:16:08 -08:00
pokeeffe-molecula
93d8bb0bca added a test to cover the keyword replace as being synonymous with insert (#2261) 2022-11-15 11:16:01 -08:00
pokeeffe-molecula
b3753c3c5d tightened up is/is not null filter expressions (FB-1741) (#2260)
Covers tightening up handling filter expressions that contain is/is not null ops. These filters may have to be translated into PQL calls to be passed to the executor and even though sql3 language supports nullability for any data type, currently only BSI fields are nullable at the storage engine level (there is a ticket to add support for non-BSI field here FB-1689: IS SQL Argument returns incorrect error) so when these fields are used in filter conditions we need to handle BSI and non-BSI fields differently.
2022-11-15 11:14:35 -08:00
pokeeffe-molecula
957ba4086b fb-1729 Enriched Table Metadata (#2255)
enriched metadata for tables

added support for the concept of a table and field owners in metadata; mechanism to derive owner from http request metadata; metadata for table description
2022-11-15 11:14:25 -08:00
Seebs
b3ffab6928 don't obtain stack traces on rbf.Tx creation
We thought stack traces were mildly expensive. We were very wrong.
Due to a complicated issue in the Go runtime, simultaneous requests
for stack traces end up contending on a lock even when they're not
actually contending on any resources. I've filed a ticket in the
Go issue tracker for this:

	https://github.com/golang/go/issues/56400

In the mean time: Under some workloads, we were seeing 85% of all
CPU time go into the stack backtraces, of which 81% went into the
contention on those locks. But even if you take away the contention,
that leaves us with 4/19 of all CPU time in our code going into
building those stack backtraces. That's a lot of overhead for a
feature we virtually never use.

We might consider adding a backtrace functionality here, possibly
using `runtime.Callers` which is much lower overhead, and allows us
to generate a backtrace on demand (no argument values available,
but then, we never read those because they're unformatted hex
values), but I don't think it's actually very informative to know
what the stack traces were of the Tx; they don't necessarily reflect
the current state of any ongoing use of the Tx, so we can't necessarily
correlate them to goroutine stack dumps, and so on.
2022-11-15 11:14:14 -08:00
Pranitha-malae
1c6bc781f3 resolving bool null field ingestion error (#2254)
* resolving bool null field ingestion error

* testing issues

* adding null support for bools

* updating the null bool field ingestion

* trying to resolve issue when ingesting null value for bool type

* adding a clearing support for bool type

* resolving issues with bool null value ingestion

* updating the jwt go package version and removing changes made in docker compose file

* reverting jwt go version

* removing v4 of jwt

* adding a comment in test file to see if sonar cloud accepts this file
2022-11-15 11:13:42 -08:00
Pranitha-malae
f4dc39bba9 Sync 800750c746 through 9b91023e29 2022-11-15 11:12:19 -08:00
Travis Turner
0e769b1da3 Sync 800750c746 through 9b91023e29 2022-11-15 11:11:40 -08:00
Seebs
f6eacec56c Sync through 011174b631 2022-11-15 11:00:50 -08:00
Seebs
7a58dfe889 Sync through 346dbb04fe 2022-11-15 10:59:13 -08:00
CLoZengineer
dc27e3d1d8
feat: builds docker images for running featurebase from a release (#2193)
* adds dockerfile for building images to run examples on

* adds makefile entries to build and push docker runner images

* cleans up tar artifacts when building ingest runner

* moves executables to /usr/local/bin

* moves the sources for building runner images into their own folder

* simplifies makefile and adds directions to readme
2022-11-08 13:07:17 -05:00
CLoZengineer
daceee2ab6
merge: featurebase merge updates for 2022-10-28 (#2188)
* use t.Fatal(f) to abort tests, not panic

* make perf_able run at all, make it debug a bit better

switch perf-able to using same node type we use for other spot instances,
because otherwise it never finds any available capacity.

we switch the perf-able script to use the standard get_value function
instead of direct jq calls.

we try to grab server logs if the restore fails in the hopes of finding
out why the restore very occasionally fails.

* Fix some issues with running IDK tests in docker. (#2248)

*Stop running TestKafkaSourceIntegration with t.Parallel()

This test can't be run in parallel as it's currently written. Doing so
allows for interleaving of messages to the same kafka topic between
tests.

I didn't attempt to modify the test so it could be run in parallel. That
could be done, but left for someone more ambitious.

* Remove idk/testenv/certs which got accidentally committed.

also update .gitignore to include those.

* changes to add bool support in idk (#2240)

* initial changes to add bool support in idk

* modifying some default parameters for testing, will revert them later

* adding support for bool in making fragments function

* boolean values implementation without supporting empty or null values at this point

* Implement bool support in batch using a map (and a slice for nulls) (#2247)

* Implement bool support in batch using a map (and a slice for nulls)

* Keep the PackBools default for now

But set it explicity in the ingest tests which rely on it.

* Modify batch to construct bool update like mutex

The code in API.ImportRoaringShard has a switch statement which causes
bool fields to be handled like mutex fields. This means, that the
viewUpdate.Clear value should only contain data in the first "row" of
the fragment, which it will treat as records to clear for *all* rows.
This makes more sense for mutex fields; for bool fields, there's only
one other row to clear. But since the code is currently handling them
the same, we need to construct viewUpdate.Clear such that it conforms to
that pattern.

This commit also adds a test which covers this logic.

* Remove commented code; revert config for testing

This commit also removes the DELETE_SENTINEL case for non-packed bools,
since that isn't supported anyway.

* Revert default setting

* remove inconsistent type scope

* correcting the logic of string converstion to bool

* resolving an error in a test

* adding tests to cover code related to bool support in batch.go file and interface.go files

* modifying interfaces test

* added one more test case

Co-authored-by: Travis Turner <travis@pilosa.com>
Co-authored-by: Travis Turner <travis@molecula.com>

* resolving bool null field ingestion error (#2254)

* resolving bool null field ingestion error

* testing issues

* adding null support for bools

* updating the null bool field ingestion

* trying to resolve issue when ingesting null value for bool type

* adding a clearing support for bool type

* resolving issues with bool null value ingestion

* updating the jwt go package version and removing changes made in docker compose file

* reverting jwt go version

* removing v4 of jwt

* adding a comment in test file to see if sonar cloud accepts this file

* don't obtain stack traces on rbf.Tx creation

We thought stack traces were mildly expensive. We were very wrong.
Due to a complicated issue in the Go runtime, simultaneous requests
for stack traces end up contending on a lock even when they're not
actually contending on any resources. I've filed a ticket in the
Go issue tracker for this:

	https://github.com/golang/go/issues/56400

In the mean time: Under some workloads, we were seeing 85% of all
CPU time go into the stack backtraces, of which 81% went into the
contention on those locks. But even if you take away the contention,
that leaves us with 4/19 of all CPU time in our code going into
building those stack backtraces. That's a lot of overhead for a
feature we virtually never use.

We might consider adding a backtrace functionality here, possibly
using `runtime.Callers` which is much lower overhead, and allows us
to generate a backtrace on demand (no argument values available,
but then, we never read those because they're unformatted hex
values), but I don't think it's actually very informative to know
what the stack traces were of the Tx; they don't necessarily reflect
the current state of any ongoing use of the Tx, so we can't necessarily
correlate them to goroutine stack dumps, and so on.

* fb-1729 Enriched Table Metadata (#2255)

enriched metadata for tables

added support for the concept of a table and field owners in metadata; mechanism to derive owner from http request metadata; metadata for table description

* tightened up is/is not null filter expressions (FB-1741) (#2260)

Covers tightening up handling filter expressions that contain is/is not null ops. These filters may have to be translated into PQL calls to be passed to the executor and even though sql3 language supports nullability for any data type, currently only BSI fields are nullable at the storage engine level (there is a ticket to add support for non-BSI field here FB-1689: IS SQL Argument returns incorrect error) so when these fields are used in filter conditions we need to handle BSI and non-BSI fields differently.

* added a test to cover the keyword replace as being synonymous with insert (#2261)

* update molecula references to featurebase (#2262)

Co-authored-by: Seebs <seebs@molecula.com>
Co-authored-by: Travis Turner <travis@pilosa.com>
Co-authored-by: Pranitha-malae <56414132+Pranitha-malae@users.noreply.github.com>
Co-authored-by: Travis Turner <travis@molecula.com>
Co-authored-by: pokeeffe-molecula <85502298+pokeeffe-molecula@users.noreply.github.com>
Co-authored-by: Stephanie Yang <stephanie@pilosa.com>
2022-10-28 13:08:23 -04:00
CLoZengineer
fc1a5dad53
feat: updates shardwidth.Exponent to single constant (#2185)
* removes build time constant in favor of single constant with relevant godoc and warnings

Co-authored-by: Christopher Lowenthal <christopher.lowenthal@molecula.com>
2022-10-25 13:30:00 -04:00
CLoZengineer
f4b58043ba
chore: adds release workflow with basic build step (#2182)
* adds release workflow with basic build step

Co-authored-by: Christopher Lowenthal <christopher.lowenthal@molecula.com>
2022-10-25 10:41:39 -04:00
CLoZengineer
25dbbb2950
chore: adds golangci config (#2174)
* removes rewrite-rules, sets gofmt.simplify to default true

* moves go vet step before the golangci-lint step

* fixes go vet issues with test files

* updates gocognit.min-complexity to default of 30

* removes deprecated options, primarily around run.go version
2022-10-17 11:42:33 -04:00
jonathan.cheng
4e0a844cfb Sync from internal repo through 6035345 2022-10-13 14:44:07 -07:00
Seebs
d4a03f21fb test for correct state during test startup
When we've started a fake cluster, we should expect to reach a
"STARTING" state, not a "DOWN" state. This test would coincidentally
pass as long as we checked the state before any of the nodes got
their notification from the node watcher that at least one node was
STARTING, because prior to that the cluster would be DOWN. But once
it got to STARTING, we would wait forever; we never reached the
instruction to tell the nodes to come to any other state, and they
would never reach a DOWN state.
2022-10-13 14:40:34 -07:00
Julio Martinez
0d7f676ce5 Change featurebase user home path to /var/lib/featurebase. (#2242) 2022-10-13 14:39:54 -07:00
Jacob Brinlee
c5aa05362a adding foreignIndex conf opt (#2239) 2022-10-13 14:39:20 -07:00
Hoang Pham
631d7e84dc FB-1696 - fixed debug message to show topic's name instead of topic's address 2022-10-13 14:38:41 -07:00
Seebs
9b3e236ce5 drop test timeouts to reasonable values 2022-10-13 14:37:55 -07:00
Kasey
5f354dfe1e
Update README.md
Update links
2022-10-13 13:41:54 -07:00
Kasey Rodgers
e75abc3c38 added testify dependency 2022-09-30 11:31:37 -07:00
pokeeffe-molecula
07da547634 moved file from this repo to documentation repo (#2232) 2022-09-30 11:25:27 -07:00
Julio Martinez
3012be083f Remove 30sec restart wait. (#2231) 2022-09-30 11:25:27 -07:00
pokeeffe-molecula
e24978c4a7 And now....INNER JOIN! (#2230)
* ID sql3 internal type representation is int64; fixed a bug that assumed incorrectly that it wasn't

* refactored some names for clarity

* primary: get nested loop joins to work; secondary get  brute force aggregations for SUM working

* added tests; removed debug output

* review feedback

* Update sql3/planner/compileselect.go

review feedback

Co-authored-by: Travis Turner <travis@pilosa.com>

Co-authored-by: Travis Turner <travis@pilosa.com>
2022-09-30 11:25:27 -07:00
HHans09
f194cb216b FB 1646 : Code updated to make the debug message more helpful 2022-09-30 11:25:27 -07:00
HHans09
c60a037065 FB-1646 : UPdated code post code review 2022-09-30 11:25:27 -07:00
HHans09
ad7a41fa06 FB-1646 Removed Debugf messages that does not make sense 2022-09-30 11:25:27 -07:00
HHans09
93c66601be UPdated the code to remove declared but unused variables - committedOffsets, stv & iv 2022-09-30 11:25:27 -07:00
HHans09
d8de30418e Removed Debugf messages that does not make sense 2022-09-30 11:25:27 -07:00
Seebs
331b5a0e36 use testhook test cleanup
The testhook post-test hooks only work if you use a TestMain to
invoke them, otherwise the cleanups can be registered but never
actually get run. This deletes the etcd sockets, and temp
directories, that we created from our test runs. We also fix
the test creating a temp file directly to create it in a TempDir
(which gets cleaned up after the test), and fix the name of the
top-level tests displayed in TestMain.
2022-09-30 11:25:27 -07:00
Seebs
20b018c0b2 remove a commented-out test case for a function that no longer exists 2022-09-30 11:25:27 -07:00
Seebs
44314fc32f drop unused row
This looks like leftover code from an earlier draft. We weren't
using this value.
2022-09-30 11:25:27 -07:00
Seebs
6b008beaec address multiple staticcheck issues
staticcheck notices a bunch of unused values and similar
things, let's fix them while we're here.
2022-09-30 11:25:27 -07:00
Seebs
d4e637eb9b drop ioutil
The ioutil package is deprecated, with all of its functions having
moved into os or io. Do the replacements so we stop having this
impending.
2022-09-30 11:25:27 -07:00
Seebs
ea69b0637d significant refactor of test setup and teardown
We centralize the creation paths for test indexes, fields,
etcetera so they all have a common path, all using standard
test holders. There's still two versions, one for test.* functions
and one for internal. They do share a TestHolderConfig though.

Large hunks of the related APIs are simplified/streamlined.
* Fragments are always created with a Field and don't need
  a workaround in case they don't have it.
* Creation of test fragments, etc., use optional FieldOptions
  but don't specify names because they're all using new holders
  for each thing created anyway. This dramatically reduces
  the complexity of the calls.
* test fragments are created inside test views which are created
  inside test fields, etcetera, so everything is using the same
  logic; test views aren't bypassing the other layers, they're
  creating themselves normally within a field.
* Quite a few things now use the standard runtime/production
  logic instead of being custom workarounds; for instance, instead
  of `mustOpenMutexFragment` creating a fragment and then creating
  a mutex vector for it, we just create a mutex-typed field and
  have the normal runtime code do this.
* Similarly, we now use the same field creation logic that production
  does, instead of having our own test-only thing that validates
  field names directly, so our test that we're validating field names
  is actually testing the runtime code. Yay.
* fragSpec goes away. it was a replacement for fragProxy which existed
  to solve memory allocation problems but replaced them with interface
  overhead problems. Now we just have pointers to things and maintain
  valid data structures.
* Many panics are now Fatal or Fatalf calls.
* Some specific bugs fixed, like a cluster which was requested and
  then had its first node directly overwritten, which isn't valid with
  shared clusters.
* Drop the temp-dir test flag and TempDir variable, we can just use
  $TMPDIR.
* Drop a benchmark of "write file to disk" that was purely a benchmark
  of file write speed, not a benchmark of rendering the data that needs
  to be written.
* Drop the unused "flags" parameter to fragment creation, which was
  only used back when we changed the BSI format.
* Use holder.Txf() rather than index.Txf(). The TxFactory has to be
  holder-level anyway, referring to it via the index is misleading.
* Test holders automatically close themselves and delete themselves,
  we remove various other things that thought they were responsible
  for deleting themselves.
2022-09-30 11:25:27 -07:00
Seebs
bfe52fb900 reduce verbosity of retryablehttpclient
The default client appears to be pretty spammy and flood us with
debug messages about POST and GET requests, and honestly we don't really
need these or benefit from them, I don't think, so let's not.
2022-09-30 11:25:27 -07:00
Seebs
f48733d44d set directory permissions restrictively to quiet etcd 2022-09-30 11:25:27 -07:00
Seebs
367f68f443 restore commented-out test 2022-09-30 11:25:27 -07:00
Seebs
787c9da90b drop excessively verbose messages used while debugging something long ago 2022-09-30 11:25:27 -07:00
Seebs
e8e655cf05 continue removing Tx parameters to view-type functions
A few view functions were taking a Tx, which had to be shard-specific,
but that's sort of awkward -- the view is inherently not shard-specific,
so it should be handling sharding internally.

There were also a couple of remaining obsolete checks for whether a
Tx was nil, at least two of which were in contexts where it absolutely
can't be. Remove all of them, and also the function itself.
2022-09-30 11:25:27 -07:00
rachithrr
e33ab1f92a FB-1674: Kafka consumer stops reading messages from topic (#2222)
Added condition to first sort by topic, followed by partition and offset.
2022-09-30 11:25:27 -07:00
pokeeffe-molecula
448fc81c2e fb-1075 (#2221)
* handle multi field count correctly

COUNT() should ignore null values.
If the data type of the expression supports an existence bitmap for the underlying FeatureBase data type we will use it to eliminate nulls from the aggregate

* simplify aggregate for existence test

we can use a direct != null instead of an indirect not(=null), and
avoid relying on the probably-broken behavior in the executor that
tries to silently fix up Row(x=3) tests on BSI fields which wanted
Row(x==3).

Co-authored-by: Seebs <seebs@molecula.com>
2022-09-30 11:25:27 -07:00
Seebs
bf6d0c1c21 use timeout when waiting for cluster state changes
We had this fail in CI once, and failing took 30 minutes because
we didn't have a timeout on this. This shouldn't ever fail, but
the fact that it did indicates that the fabled etcd failures
we've seen a couple of times were still capable of happening.
This will make that failure happen sooner and more clearly.

Also, log the cluster states (and possibly node states) while
waiting. But add a delay -- otherwise we can do this quite a few
times per millisecond. We use Logf so that, if you didn't use -v,
you see these reported only if the test fails, but if the test fails,
we'll say what happened.

It would probably be better to have a passive thing that can wait
for updates, because we're waiting on heartbeats. Missing: A way to
detect what's actually happening in the failure cases, which we
see only quite rarely.
2022-09-30 11:25:27 -07:00
Seebs
e3afc50d99 staticcheck is right, as usual 2022-09-30 11:24:56 -07:00
Hoang Pham
232fcd5151 FB-1663 - fix make build-lattice failing 2022-09-30 11:24:49 -07:00
Hoang Pham
f9945a403f FB-1339 - UI - updated lodash version to fix gitlab vulnerability 2022-09-30 11:24:43 -07:00
Seebs
130eea3aaa bump to go 1.19.1 because there's no longer a docker image for 1.19 2022-09-30 11:24:32 -07:00
Seebs
cd6125dfc6 squash auth tests into regular smoke tests, improve smoke tests
This is a bit complicated and entangled, sorry.

First, we squash the auth-based smoke tests into the regular smoke
tests; we just run all the tests with auth on and that way we don't
need to spin up an entire separate cluster of machines just to run
a single query against them.

We improve the error detection, and standardize the jq-to-get-config
code. The purpose of this is to try to make sure that, if we actually
hit a failure and get "null" for a host name, we report *that*
as an error, rather than running ahead and producing 20+ separate
reports that ssh failed because it couldn't find a host named null.
2022-09-30 11:23:17 -07:00
Seebs
3671129cd5 don't run commands against holder before messing with its stats
You have to start the cluster before you can refer to its holders,
because GetNode doesn't work on an unstarted cluster, but if you
actually issue any commands, those require messing with the worker
pool which wants to have access to the holder's stats.
2022-09-30 11:22:07 -07:00
Matthew Jaffee
01a0eb0d74 add persistent history and combination of multi-line commands to CLI
"featurebase cli" will now save command history to
$HOME/.featurebase/cli_history by default. Additionally if a command
is entered across multiple lines, the newlines will be removed when
the command is saved in the history. Previously each line was saved
separately which was a bit annoying.
2022-09-30 11:22:07 -07:00
pokeeffe-molecula
7bbfc8e5e9 Add a smoke test for the sql3 endpoint (#2214)
* turned on sql endpoint in smoke test; added a test to execute a simple sql statement

* add config to both configs

* fixed not enough arraying
2022-09-30 11:21:56 -07:00
Garrison Davis
0d8393ddcf Fix UUID library version for CVE-2021-3538 (#2213)
We were using v1.2.0 of the github.com/satori/go.uuid library to
generate UUIDs for transactions if the transaction had no previous id.

That version of the library had CVE-2021-3538: "Due to insecure
randomness in the g.rand.Read function the generated UUIDs are
predictable for an attacker."

More reading can be done here:
https://pkg.go.dev/vuln/GO-2022-0244
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-3538

This vulnerability was found using the new govulncheck tool which is not
currently used in our CI pipeline but might be a good candidate to
include in the future. (Like all tools like this there are caveats to
its usage and utility which can be read about below.)

Information on that tool can be found here:
https://go.dev/blog/vuln
https://pkg.go.dev/golang.org/x/vuln/cmd/govulncheck
2022-09-30 11:21:11 -07:00
pokeeffe-molecula
15382a2863 sql3 changes (#2211)
* first cut of working (slowly) bulk insert; table valued functions and a tuple data type to support time quantums

* oversight

* filter pushdown implementation; bulk insert

* addressed some linter issues
2022-09-30 11:21:11 -07:00
Seebs
e47bdb7889 cleaned up sync from private repo 2022-09-30 11:20:58 -07:00
Seebs
f6d17b1b58 refactor testing to share clusters more often
When doing tests, we create a ton of one-off clusters. This
turns out to be expensive and slow. Fixing it is surprisingly hard.

Fundamentally: If we're sharing clusters, we need to use different
indexes for each test, to avoid clashes. This changes index names.
As a side-effect, this reorders many partition-based things, like
the order keys are returned in. Thus, to fix this, we change a lot
of tests to no longer depend on the *order* in which strings are
returned.

Having done that, we can also discard the ModHasher behavior, since
that only existed to allow us to reliably predict partitioning.

The basic design is as follows: Instead of a cluster being a
[]*Command, a "shareable" cluster is now a []*Command plus some
flags, and a "cluster" is a pointer to a possibly-shared cluster,
plus a link to the specific test using this specific cluster,
and correspondingly, its test name suitably coerced to be a valid
index name prefix.

The "test.Cluster" object now has methods to allow retrieving an
index name, and also implemnts fmt.Formatter to let you use,
e.g., `%i` with it in Sprintf to get "the index name, plus an i".
(This works for everything but %p and %T.)

This allows us to consistently rework all the many things that
use index names in a persistent way.

We also have `MustUnshared` and `MustRunUnsharedCluster` methods
which allow us to specify that a given test needs its own cluster
for some reason. For instance, the tests that want to run backups
need their own isolated cluster, and the tests that want to close
or reopen nodes need their own cluster because a reopened cluster
won't have working GRPC for some reason.

On "closing" a shared cluster (actually the test-specific wrapper
that reflects a given sharing), we delete any indexes starting with
that test's index name prefix. Otherwise, the huge pile of open
indexes prevents `go test -race` from working on MacOS, where we
run out of address space too quickly.

This is fairly enormous but most of the individual changes are
fairly trivial things like replacing the string "i" with "c.Idx()".

We also tweaked a test that failed for me a couple of times to
not depend on sort order.
2022-09-30 11:10:47 -07:00
Seebs
799e70fe46 shorten SetValue_QuickCheck
We reuse a fragment. It might seem surprising that this works, but
the fragment code actually doesn't have a persistent bit depth at
all, it just accepts whatever bit depth you tell it to use. Cutting
out the recreation of the fragments saves some time.

We also cap bit depth at 8, instead of 62, because there's a ton
of runtime cost to testing more bit depths, but it doesn't actually
change the logic.
2022-09-30 11:10:47 -07:00
Seebs
904b19dce1 reduce simultaneous memory usage a bit
Delete indexes after we're done with them from subtests so that
we don't fill up on a huge number of unrelated indexes.
2022-09-30 11:10:47 -07:00
Seebs
fb3e60f88a set RBF sizes smaller for test
We end up hitting race detector limits on MacOS. This should mitigate
that.
2022-09-30 11:10:47 -07:00
Seebs
dad8211709 fix silly math typo
For arbitrary mod values m, greater than zero,
	(x%m + 1) != 0
is always true

What we almost certainly meant was
	x%(m+1) == 0

which would give you all the bits in row 0, half the bits in row 1,
etcetera.

Also, we drop to doing a quarter-shard because why not.
2022-09-30 11:10:47 -07:00
Seebs
6207da9c48 reduce size of KeyReplication test
This test used to be large, because it was testing some features that
were refactored out in October of 2019. Since we no longer have the
"buffer growth" to check, let's check a much smaller file.
2022-09-30 11:10:47 -07:00
Seebs
c1ee7d5a5c improve post-hook behavior
We want to be able to register hooks which do cleanup, which may be
registered after the auditor cleanup check, which means that we
want LIFO order for post-hook cleanups.

We also want the test hook cleanup to be deferred, rather than
merely run after the tests are executed. Also, we have to extract
the result from running the test, then execute deferred things,
*then* call os.Exit, because os.Exit bypasses defers.
2022-09-30 11:10:47 -07:00
Seebs
964e7d86c8 kill everything that tries to pass nil tx to field/view things
The "field/view will just synthesize a tx" behavior is awful and
also hides a number of fundamental flaws. We distinguish between
"we really do mean to work on a single shard here" and "we intend
to work on the whole field or view", and the latter now take
Qcx instead of Tx.

This eliminates a lot of very weird cases where we checked for
nil Tx and synthesized them, and also gets us away from
field and view taking Tx parameters when no possible Tx
can be constructed which is valid, because Tx are inherently
shard-specific at this time.
2022-09-30 11:10:47 -07:00
pokeeffe-molecula
204558b46f added /sql endpoint; implemented SHOW TABLES (#1935)
* squashed 45 commits into one :)

* tlt/sql experiment (#2035)

* Move PlanOperator to sql3/planner/types package

includes:
type PlanOperatorColumn struct
type PlanOperator interface

* Remove planner dependencies from pilosa package

The goal after this is to prevent the planner package (which doesn't exist yet)
from being imported by the pilosa package; we just want it injected into the server
in server/server.go. This is because the planner package uses pilosa types, so we need
to avoid circular dependencies.

Added ExecutionPlannerFn
Make public: pilosa.ExecOptions
Added a pilosa.Executor interface
Added a planner.types.CompilePlanner interface
Isolated the planner calls to:
- Executor.Execute()
- *API.[method]()

* Move executionplanner files into the sql3/planner package.

This required a bit of gymnastics, and there are some things around
FieldOptions which need to be addressed soon.

* Remove the hacky FieldOptions stuff I added earlier

This implementation just uses the pilosa.FieldOption functional options
provided by the API (as opposed to trying to build a FieldOptions
object.

It also changes field types to constants. These are private for now, but
if we need to make them public, we should put them in the planner/types
package.

* Implement the "scale" value from Decimal(scale)

Also, precision and scale were currently reversed in the parser. This
fixes that.

* Modify the parser to handle CACHETYPE <type> SIZE <size>

It's a little odd to me that the cache type values are Tokens, but I
guess it's ok. One thing to keep in mind is that FeatureBase expects
lowercase values, so this commit changes the parser to set the value to
the lowercase version of the type.

* Fix the /sql2 tests

This entailed a combination of commenting out or t.Skip()-ing tests
which covered code in the parser that has been commented out or removed
as not currently supported in sql3.

It also adds some coverage for the sql.Contraint stringers.

* Prevent JSON sql results from containing closing commas

This commit just re-works the existing output code to avoid inserting
closing commas (which results in invalid JSON).

* Enhance the CREATE TABLE test coverage.

In particular, ensure that the fields which get created in FeatureBase
are what we expect based on the fields defined in the CREATE TABLE
statement.

This also ensures that the TIMEQUANTUM and CACHETYPE contraints are not
provided for the same field (since those constraints are not supported
together).

* Adjust the EBNF file to indicate SIZE contraint is optional

A CACHETYPE can be provided without a SIZE. This change indicates that
SIZE is optional.

* Remove `executionplanner_` from file names (#2040)

* implementation of ALTER TABLE (sans column RENAME)

* refactored expression analysis; added more robust type checking; all unary and bin ops function on ints

* added type support for expressions; full bin/unary op support; added cast; more literal support

* cast int to all other types

* all literals (except idset, stringset & timestamp) make it thru; cast to all types with int as source now works

* implemented LIKE/NOT LIKE

* Implemented IS [NOT] NULL

* Move sql2 files into sql3/parser package (#2045)

* Move sql2 files into sql3/parser package

This also removes the sql2 package.

* Fix tests which were typing _id fields as INT intead of ID

* implemented BETWEEN, NOT BETWEEN

* Add featurebase/error package (#2046)

* Add featurebase/error package

I copied the `dax/errors` package which I am starting to use in the DAX
prototype into `featurebase/errors` in order to start using it with the
sql3 package. It's basically a wrapper around `github.com/pkg/errors`,
but it uses a customer coded error.

The sql package can define its own errors based on the
`featurebase/errors` types. Then do things like `Wrap()` and `Is()`.

* Address the linter complaints: shadowed variables, unreachable code

* implemented IN & NOT IN with expression lists

* first cut of CASE

* Fixed some errors from rebase

* updated bnf; removed unused code; tightened up error handling

* first crack at basic CLI for SQL3

Use: `featurebase cli`

Still lots to do here, but for example:

> select count(*) from tremor
+--------------+
|        COUNT |
+--------------+
| 1.158321e+06 |
+--------------+

* Iterate on the CLI (#2057)

Handle the errors.
Add an "exit" command.
Add some general formatting and white space.

Add termination character: ";" (semicolon)

This commit allows a user to provide multiple or partial SQL statements.

Example of multiple statements:
```
show tables; select * from foo;
```

Example of partial (multi-line) statements:
```
select *
from foo;
```

Don't uppercase the header values

* error refactoring; first cut of TOP; remove unused code; use log.Printf instead of fmt.Printf

* fixed a bug with QualifiedRef from refactoring; added bones of INSERT; removal of unused code; tightened up errors more; fixed failing tests

* single value list for INSERT

* Update bnf per discussion with Travis; INSERT now doing the requisite stuff

* Pat's eyes went square - nothing wrong with TOP, Pat needed to learn arrays again.

* improved some errors; fixed tests to suit

* send warnings back in the api; update CLI to display warnings

* start warning on stuff not implemented so we don't get bugged about it

* Tlt/sql experiment (#2063)

* Expresssion -> Expression

* Add SQL planner test

- adds a test to which it is easier to add tables and SQL statments
- un-exports all of the expression types
- removes the planner pointer from the expression types (it can be added
  back later if need be)

* Fix where clause on a string field

Prior to this commit, the binary expression for a where clause on a
string field was building the call by providing a range operator which
is typically used for BSI fields. This changes it to use the call.Args
for string values.

* Update planner tests to handle multiple sql for the same results

* Reorganize SQL tests

Introduce a test/helpers package and move shared MustQueryRows into that
package.

* Add a compatibility map for field types. (#2064)

This is primarily to address the fact that ID fields were previously
incompatible with INT literals.

We should probably consider introducing a custom type for FieldType
which can be used to define compatibilities.

* significantly refactored type checking

* Handle nil (NULL) values in the sql CLI. (#2067)

go-pretty panics if the interface{} field value is nil. This replaces
nil values with a "NULL" string.

* Squash some commits

fixed a still failing test

added line, col to all error messages

refactored source handling to enable table aliases

fixed some copypasta per review

warnings for order by & topn; implemented select as a source

starting to handle in (select...); added stub for optimizer

JSON-encode the sql error and warning strings (#2069)

Error strings with unencoded characters (like double quotes) were
resulting in invalid json.

got insert working; added symbol table; added concrete optimizer; added nascent NestedLoopsOperator; rewrite "where foo in (select..." as inner join

* all about the sets (#2085)

* implemented setcontains()

* implemented set literal; insert set column values; setcontains/all/any both in expr eval and pql filters

* Convert test to use latest framework. (#2086)

* fixed some comments

* removed refactored tests

Co-authored-by: Travis Turner <travis@pilosa.com>

* Add support for Decimal fields to the sql test. (#2090)

* dates (#2094)

* return dates as strings in output; tightened up decimal type checking

* return dates as strings in output; tightened up decimal type checking

* fixed failing tests after decimal changes

* can now insert decimal values

* implemented insert for timestamp data type; implemented current_date, current_timestamp constants

* fixed some failing tests

* handle date literals from strings in insert statements

* changes from feedback

* Fix pointer method error

* sql3 API interface (#2110)

* Introduce API-related interfaces: SchemaAPI, ComputeAPI

The sql3 code was relying on the pointer: *pilosa.API in order to call
API methods directly on the local node. If we want to import and use the
sql3 package in another service (the DAX queryer, for example), we need
to be able to use an implementation of an interface for those API method
calls.

This commit introduces two interfaces, both automatically implemented by
pilosa.API:
- SchemaAPI
- ComputeAPI

* Convert sql3 code to use IndexInfo instead of Index

The sql3 code was relying on a *pilosa.Index and its methods to get
general information like index and field name, type, etc. This commit
converts everything to use a *pilosa.IndexInfo instead.

This allows us to modify the SchemaAPI interface to also return
IndexInfo instead of Index, which will be a lot easier to implement in a
non-pilosa package (like DAX); creating a *pilosa.Index requires
providing things like data directory paths and holders, which are not
necessary for these use cases.

* Unary and Binary Ops R US plus CAST (#2111)

* implemented string literal for timestamp epoch

* fixed failing test

* fixed the failing test again

* refactored tests; implemented unary op tests for all datatypes; implemented binop tests for int/int, int/id, int/decimal & ID/int

* implemented all binary ops for INT & all other types, ID & all other types

* implemented binary ops for DECIMAL types & all other types

* added STRING & BOOL to various tests; implemented all remaining binOp tests

* fix up some stuff after rebasing

* refactored test defs into multiple files; implemented CAST for every datatype

* added tests for like/not like

* addressed review feedback

* addressed type review feedback

* tightened up IS [NOT] NULL behavior plus tests (#2118)

* tightened up IS [NOT] NULL behavior plus tests

* BETWEEN/NOT BETWEEN with all data types

* addressed review feedback

* Handle negative integers in column min/max constraints (#2120)

This commit parses the min/max contraint as an expression, as opposed to
an int literal, so that negative values are treated as Unary
expressions.

There currently isn't support for min/max constraints on `decimal`
fiels, so for now this change only expects +/- integer values.

* Implement the CREATE TABLE keypartitions logic (#2123)

* Execution time, IN/NOT IN & multiple aggregates (#2124)

* added display of execution time

* IN/NOT IN tests for all data types

* fixed date parsing

* removed duplicative tests

* refactoring aggregates

* suport multiple aggregates

* Address review feedback

* final round of feedback

* Add method SchemaAPI.CreateIndexAndFields() (#2127)

In order to support a CREATE TABLE statement as a single command, this
commit alters the SchemaAPI interface to contain a single method which
handles both the index and its fields. It also updates the sql3 code to
use this interface instead of CreateIndex() and CreateField()
indepedently.

* Symbol Handling (Again) (#2129)

* Refactored symbol handling in the planner; re-instated the select as source tests

* removed commented out code

* addressing review feedback

* Move hard-coded _id field out of planner and into interface implementation (#2130)

This commit moves the hard-coded addition of the `_id` field from the
planner to the SchemaAPI.IndexInfo() implementation method.

NOTE: If anything was expecting SchemaAPI.Schema() to also return the
`_id` field as part of its field list in each table, then it would not
be there because the `_id` field is only added in the IndexInfo() method
for now. Currently that's not a problem because nothing is expecting the
`_id` field for `Schema()`.

* Multiple aggregates, all aggregates stand alone and in GROUP BY (#2132)

* handle multiple aggregates in group by queries

* added handling for avg() aggregate both stand alone and in group by

* tightened up sum & avg outside of group by

* added min, max & percentile

* added warnings

* Make MaterializedRowSet implement the PlanOperator interface. (#2133)

This commit refactors the PQLMultiGroupByOperator to have a PlanOperator
as its output. Then, when it initializes, it sets up a
MaterializedRowSet and populates that with the values from the multiple
group by operations.

* added explicit min/max pql operators

* saved a file I forgot to save

* per review

* Un-indent some if/else nesting (#2136)

Co-authored-by: Travis Turner <travis@pilosa.com>

* Add optional `name` argument to test structs.

This commit adds the `name` argument to `tableTest` and `sqlTest` so
that a test can be optionally named. This allows a developer to more
easily run/identify a particular test by name.

* Inbuilt functions (redux) (#2141)

* set functions type parameter type checking

* implemented datepart

* include SQL3 type in SHOW COLUMNS output

* fixed select as source; failing SHOW COLUMNS test

* select in select list

* dump output columns; handle optimization for select list subqueries

* make it an error to return multiple rows for a select list subquery

* added description

* contants and test coverage for datepart function

* SQL3 Refactor-palooza (#2182)

* removed unneeded IsAggregate()

* first cut of working nested loops operator aka INNER JOIN

* remove selectListItemPlanExpression

* added some warnings

* all the tests are passing again!

* addressed some linter complaints

* added basic order by

* bug fixes; added 'or replace'/'replace' to insert

* for insert references should return appropriately

* added back ability to use subquery singleton expressions

* removed dead code; fixed test

* json-able plan, Schema() plus refactoring

* fixed dumb code

* add some tests for time quantum behavior

* Code cleanup during review. Also fixed INSERT to keyed table bug.

This commit contains a lot of minor adjustments made during code review.

It also contains a bug fix that was preventing INSERT into a keyed table
(i.e. _id type STRING) from working.

Co-authored-by: Travis Turner <travis@molecula.com>

* Fix expected min/max on timestamp column test (decimal field)

I don't know why this changed, but presumably something to do with
decimal related work that happened on master.

* Fix compile problem after rebase

* review feedback

Co-authored-by: Matthew Jaffee <jaffee@pilosa.com>
Co-authored-by: Travis Turner <travis@pilosa.com>
Co-authored-by: Travis Turner <travis@molecula.com>
Co-authored-by: Fletcher Haynes <fletcher@capitalprawn.com>
2022-09-30 11:10:24 -07:00
Hoang Pham
8b8f14a6bc FB-1627 - Added backup/restore tar. Purpose: for cloud team to backup and restore directly through stdout, stdin 2022-09-30 11:06:32 -07:00
CLoZengineer
f9ddb5d5c1
fix: updating code to meet linting requirements (#2171)
* removes unused filesize function

* removes ioutil usage

* updates ioutil.ReadAll to io.ReadAll

* updates ioutil.TempFile to os.CreateTemp

* updates ioutil.TempDir to os.MkdirTemp

* updates ioutil.ReadAll to os.ReadAll

* update ioutil.WriteFile to os.WriteFile

* updates ioutil.Discard to io.Discard

* updates ioutil.ReadDir to os.ReadDir where applicable

* removes unused code in idk

* creates type to use for context value keys

* replaces assert.Nil with assert.NoError for error checks
2022-09-29 12:34:29 -04:00
CLoZengineer
51249cda78
chore: creates ci.yml workflow (#2167)
Creates our basic CI/CD workflows

Co-authored-by: Christopher Lowenthal <christopher.lowenthal@molecula.com>
2022-09-22 17:09:59 -04:00
CLoZengineer
e61ef2a4da
fix: fixes unkeyed composite literal (#2168)
Co-authored-by: Christopher Lowenthal <christopher.lowenthal@molecula.com>
2022-09-22 10:45:37 -04:00
hphamMolecula
f07dc61617
Merge pull request #2164 from FeatureBaseDB/2161-build-lattice-makefile-target-failing
Fix issue #2161 build lattice makefile target failing
2022-09-13 11:23:07 -05:00
Hoang Pham
40d9f9a834 Updated package.json to use @types/react": "^17.0.0 in devDependencies and resolutions 2022-09-08 16:20:10 -05:00
Fletcher Haynes
383ce50b95 Updated Dockerfiles for lattice to use a publicly available container in the GitHub container registry 2022-09-08 11:05:43 -07:00
Fletcher Haynes
0c19c87a40 Merge branch 'master' of github.com:pilosa/pilosa 2022-09-06 09:39:51 -07:00
Fletcher Haynes
da9b57bd45 Updated dependency paths to reflect new repo location 2022-09-06 09:39:22 -07:00
Kasey
acf94f318b
Merge pull request #2160 from FeatureBaseDB/README-corrections
chore:correct README typo
2022-09-02 14:03:28 -07:00
Kasey Rodgers
6d326a4974 chore:correct README typo 2022-09-02 13:58:44 -07:00
Fletcher Haynes
eb06bb50ae Updated code to latest version for open-sourcing. 2022-09-02 13:23:39 -07:00
Matthew Jaffee
227632544d
Merge pull request #2150 from pilosa/slack-link
Add slack link to readme
2021-12-14 10:56:22 -06:00
Alan Bernstein
0c39f7e4ea
Add slack link to readme 2021-12-13 20:16:11 -06:00
Travis Turner
8886da126f
Merge pull request #2137 from travisturner/backport-fix
fix interface{} -> bool err
2021-01-27 08:13:04 -06:00
Travis Turner
4681d50988
remove leftover commented code 2021-01-27 07:55:15 -06:00
wanggy
53c1fb6bbe
fix interface{} -> bool err 2021-01-26 21:53:13 -06:00
alanbernstein
d960d2fda9
Merge pull request #2130 from alanbernstein/temper-wsl-reference
Remove WSL references
2020-10-23 16:32:29 -05:00
Alan Bernstein
dd4572a4ca Remove suggestion of Windows support 2020-10-23 12:13:03 -05:00
Travis Turner
052440974a
Merge pull request #2125 from travisturner/fix-language-link
fix languages.txt link
2020-09-10 16:18:02 -05:00
Travis Turner
efd5663cc4
fix languages.txt link 2020-09-08 16:41:14 -05:00
Kuba Podgórski
689776b1c0
Merge pull request #2122 from kuba--/notfound-verbose
Make not found error more verbose (add name)
2020-08-27 21:56:32 +02:00
Kuba Podgórski
1f32fe05b0 Make not found error more verbose (add name) 2020-08-27 19:45:19 +02:00
Kuba Podgórski
55353a2567
Merge pull request #2119 from kuba--/union-run-run
Add unionRunRunInPlace
2020-07-16 12:37:01 +02:00
Kuba Podgórski
730fab38dc Add unionRunRunInPlace 2020-07-14 15:10:26 +02:00
Travis Turner
9dc1775b93
Merge pull request #2112 from travisturner/block-edge
Fix off-by-one maxRowID in block limits
2020-05-04 21:54:04 -05:00
Travis Turner
56adbfedfd
Fix off-by-one maxRowID in block limits 2020-05-04 17:29:53 -05:00
Kuba Podgórski
9426b9ee60
Merge pull request #2111 from kuba--/fix-groupby-iter
GroupBy should terminate even if the last result is empty
2020-04-29 16:07:39 +02:00
Kuba Podgórski
5f4a1d1668
Merge branch 'master' into fix-groupby-iter 2020-04-29 11:14:32 +02:00
Kuba Podgórski
1b3d0fd8a9
Merge pull request #2104 from sundy-li/holder-fix
holder.go: fix some logic errors
2020-04-29 11:14:11 +02:00
Kuba Podgórski
8678bc52ba
Merge branch 'master' into fix-groupby-iter 2020-04-27 17:51:13 +02:00
Kuba Podgórski
4e16a4c489
Merge branch 'master' into holder-fix 2020-04-27 17:50:26 +02:00
Kuba Podgórski
9cf9968b64
Merge pull request #2110 from seebs/roaring4go
Handle file sizes over 4GB
2020-04-27 17:49:44 +02:00
Kuba Podgórski
816b015cf3
Merge branch 'master' into holder-fix 2020-04-27 12:47:09 +02:00
Kuba Podgórski
06675c2f9c GroupBy should terminate even if the last result is empty 2020-04-27 12:43:15 +02:00
Seebs
827923e616 Handle file sizes over 4GB
We only have 4 bytes for offsets, but what if a file is
over 4GB? Someone came to us with a file with 265 *million* containers,
in a single fragment, which means that over 3GB of their 4.7GB file
is actually just the container headers alone. But we can't easily make
the offsets larger, or change the file format.

So we don't. We just track how many 4GB hunks of the file we've
been through and bump that every time the 32-bit offset wraps. And this
appears to... just work.

This is fixed for both the roaring iterator and the old unmarshalBinary
logic. The logic to handle this will work on 32-bit hosts in the sense
that it will correctly error out for excessively large file sizes or
container counts, but it doesn't actually handle the large files since
it can't.
2020-04-24 10:56:24 -05:00
Travis Turner
72d5081a44
Merge pull request #2105 from zhanglistar/master
1. modify unprotectedGenerateResizeJob to return early iif resizing job
2020-04-11 10:13:26 -05:00
Travis Turner
f4d1122fbc
Merge branch 'master' into master 2020-04-11 09:51:47 -05:00
zhanglistar
93b6adbf65 1. modify unprotectedGenerateResizeJob to return early iif resizing job
not null
2020-04-11 22:48:28 +08:00
Travis Turner
f5c2546c37
Merge pull request #2087 from travisturner/cache-size-none
Fix cacheSize when cacheType is none (and cacheSize is 0)
2020-04-09 09:29:18 -05:00
Kuba Podgórski
9cdaff542f
Update field_internal_test.go 2020-04-09 15:04:34 +02:00
Kuba Podgórski
4272693641
Update field_internal_test.go 2020-04-09 15:04:11 +02:00
Kuba Podgórski
b43065625a
Merge branch 'master' into cache-size-none 2020-04-09 12:37:44 +02:00
sundy-li
768c709937 holder.go: fix some logic errors 2020-04-08 16:35:42 +08:00
Kuba Podgórski
eb73042e82
Merge pull request #2100 from PierreF/runCountRange-bug
Fix offset-by-1 in CountRange with continuous bits interval
2020-03-17 18:57:27 +01:00
Pierre Fersing
e8ca41e522 Fix runCountRange when range start == interval start
When the interval is a proper superset of the range with start equal to
interval start, the range must be considered a superset or it will be
completly ignored (since it neither a subset nor it overlaps)
2020-03-13 10:47:39 +01:00
Kuba Podgórski
ac3a4abb4a
Merge pull request #2099 from kuba--/test-clear
Add extra Clear test (check if existency column bit is set)
2020-02-13 16:11:24 +01:00
Kuba Podgórski
377d14081a Add extra Clear test (check if existency column bit is set) 2020-02-13 15:20:03 +01:00
Kuba Podgórski
771cc3ae94
Merge pull request #2098 from kuba--/fix-2097
Update handler.go
2020-02-05 23:45:07 +01:00
Kuba Podgórski
4f0740ae4f Update handler.go 2020-02-05 22:17:45 +01:00
Kuba Podgórski
8ec1fef68a
Merge pull request #2096 from kuba--/simplify-holder
Simplify Holder's logic
2020-02-04 00:56:11 +01:00
Kuba Podgórski
b9fa6da5d2 Simplify Holder's logic 2020-02-03 18:12:15 +01:00
Travis Turner
7a032e62f0
Merge pull request #2088 from travisturner/test-2084
Add a test for pilosa/#2084
2019-11-13 10:12:21 -06:00
Travis Turner
4fb0712dab
add a test for pilosa/#2084 2019-11-12 12:05:36 -06:00
Travis Turner
8d716457d1
Merge pull request #2084 from travisturner/import-value-cache
Add logic that clears fragment.rowCache after importValue
2019-11-11 17:16:00 -06:00
Travis Turner
9273691e8d
move requiredDepth calculation after min/max ranges are checked
Without this, a data set with a ludicrously large value in it
could break a BSI field's depth even though the import would then
reject it.

always treat BSI fields as having at least their depth:

If you imported only small values, BSI fields could end up
not bothering to clear higher bits in existing values, which
produced strange behaviors.
2019-11-11 16:48:14 -06:00
Travis
4af91faa7e
fix cacheSize when cacheType is none (and cacheSize is 0)
There was an edge case where setting cacheType to none
wouldn't zero out its cacheSize. This fixes that edge case.
2019-11-08 17:12:35 -06:00
Travis Turner
020b72abbf
reset fragment.rowCache after importValue 2019-11-06 13:19:12 -06:00
907 changed files with 41510 additions and 23858 deletions

View file

@ -1,7 +0,0 @@
lattice/.git
lattice/node_modules
lattice/build
statik/statik.go
build
testenv
bin

View file

@ -1,16 +0,0 @@
For bugs, please provide the following:
### What's going wrong?
### What was expected?
### Steps to reproduce the behavior
### Information about your environment (OS/architecture, CPU, RAM, cluster/solo, configuration, etc.)
For feature requests, please provide the following:
### Description
### Success criteria (What criteria will consider this ticket closeable?)

View file

@ -1,24 +0,0 @@
## Overview
[Describe what this pull request addresses.]
Fixes #
## Pull request checklist
- [ ] I have updated the [documentation](https://github.com/molecula/docs).
- [ ] I have resolved any merge conflicts.
- [ ] I have included tests that cover my changes.
- [ ] All new and existing tests pass.
- [ ] Add appropriate changelog label to PR (if applicable).
## Code review checklist
This is the checklist that the reviewer will follow while reviewing your pull request. You do not need to do anything with this checklist, but be aware of what the reviewer will be looking for.
- [ ] Ensure that any changes to external docs have been included in this pull request.
- [ ] If the changes require that minor/major versions need to be updated, tag the PR appropriately.
- [ ] Ensure the new code is [properly commented](https://github.com/golang/go/wiki/CodeReviewComments#doc-comments) and follows [Idiomatic Go](https://dmitri.shuralyov.com/idiomatic-go).
- [ ] Check that tests have been written and that they cover the new functionality.
- [ ] Run tests and ensure they pass.
- [ ] Build and run the code, performing any applicable integration testing.
- [ ] Make sure PR is tagged with appropriate changelog label.

77
.github/workflows/cd.yml vendored Normal file
View file

@ -0,0 +1,77 @@
name: CD
on:
push:
branches: ["master"]
workflow_dispatch:
jobs:
validate:
runs-on: ubuntu-latest
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
with:
go-version: "^1.19.1"
- run: go version
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
args: --timeout=5m
- name: go vet
run: go vet ./...
- name: test
run: go test ./...
release:
needs: validate
runs-on: ubuntu-latest
outputs:
version: ${{ steps.semrel.outputs.version }}
steps:
- name: go-semantic-release
id: semrel
uses: go-semantic-release/action@v1.17.0
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
build:
needs: release
runs-on: ubuntu-latest
strategy:
matrix:
goos:
- "darwin"
- "linux"
- "windows"
goarch:
- "amd64"
- "arm64"
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
with:
go-version: "^1.19.1"
- name: build
run: GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} go build -o ./build/${{ matrix.goos }}-${{ matrix.goarch }}
- name: Upload binaries to release
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ./build/${{ matrix.goos }}-${{ matrix.goarch }}
asset_name: ${{ matrix.goos }}-${{ matrix.goarch }}
tag: ${{ github.ref }}

57
.github/workflows/ci.yml vendored Normal file
View file

@ -0,0 +1,57 @@
# This is a basic workflow to help you get started with Actions
name: CI
# Controls when the workflow will run
on:
pull_request:
branches: ["master"]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
validate-title:
name: Validate PR title
runs-on: ubuntu-latest
steps:
- uses: go-semantic-release/action@v1
id: semrel
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
dry: true
# golangci-lint must be run separately from "validate" as there are go mod issues if you run it after the vet
golangci:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/setup-go@v3
with:
go-version: 1.19
- uses: actions/checkout@v3
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
# version: v1.29
args: --timeout=8m
validate:
name: Code Checks
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
with:
go-version: "^1.19.1"
- run: go version
- name: go vet
run: go vet ./...

25
.github/workflows/release.yml vendored Normal file
View file

@ -0,0 +1,25 @@
name: Release
on: workflow_dispatch
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
goos:
- "darwin"
- "linux"
goarch:
- "amd64"
- "arm64"
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
with:
go-version: "^1.19.1"
- name: build
run: GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} go build -o ./build/${{ matrix.goos }}-${{ matrix.goarch }}

2
.gitignore vendored
View file

@ -83,3 +83,5 @@ staticcheck.conf
dax/dax-data
coverage-from-docker
*.client_id.txt

View file

@ -12,15 +12,34 @@ include:
- template: Security/License-Scanning.gitlab-ci.yml
- template: Security/Dependency-Scanning.gitlab-ci.yml
.idk_changed:
rules:
- if: '$CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "schedule" || $CI_PIPELINE_SOURCE == "web"'
changes:
compare_to: refs/heads/master
paths:
- idk/**
- client/**
- batch/**
default:
retry:
max: 2 # This is confusing but this means "3 runs at max".
when:
- unknown_failure
- api_failure
- runner_system_failure
- job_execution_timeout
- stuck_or_timeout_failure
variables:
GOVERSION: "1.19.3"
GOFUTURE: "latest"
CI_IMAGE: "${CI_REGISTRY_IMAGE}/ci-builder:0.0.1"
CI_PRE_CLONE_SCRIPT: |
set -x
stages:
- ci_image_build
- lint
- test
- build
- post build
- integration
- gauntlet
- performance
- nonblocking
- cleanup_build
gosec-sast:
allow_failure: false
@ -42,51 +61,6 @@ gosec-sast:
- go install 'gitlab.com/gitlab-org/security-products/analyzers/gosec@v1.4.0'
- gosec convert gosec.json > gl-sast-report.json
variables:
GOVERSION: "1.19.3"
GOFUTURE: "latest"
stages:
- lint
- test
- build
- post build
- integration
- gauntlet
- performance
- nonblocking
- cleanup_build
.setup_ssh:
before_script:
- export PATH=$PATH:/usr/local/go/bin:$HOME/go/bin
- export GOPRIVATE=github.com/molecula/*
## Install ssh-agent if not already installed, it is required by Docker.
## (change apt-get to yum if you use an RPM-based image)
- "command -v ssh-agent >/dev/null || ( apt-get update -y && apt-get install openssh-client -y )"
## Run ssh-agent (inside the build environment)
- eval $(ssh-agent -s)
## Add the SSH key stored in SSH_PRIVATE_KEY variable to the agent store
## We're using tr to fix line endings which makes ed25519 keys work
## without extra base64 encoding.
## https://gitlab.com/gitlab-examples/ssh-private-key/issues/1#note_48526556
- echo "$FB_SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
## Create the SSH directory and give it the right permissions
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- git config --global --get url."ssh://git@github.com/".insteadOf || git config --global --add url."ssh://git@github.com/".insteadOf "https://github.com/" || true
## Set up known_hosts so we don't get prompted when there's no
## human to answer the prompt (resulting in cryptic failures).
## ssh-keygen -F checks whether github.com is in known_hosts and
## handles HashKnownHosts appropriately.
- ssh-keygen -F github.com || echo "$SSH_KNOWN_HOSTS_HASHED" >> ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
.go-cache:
variables:
GOPATH: $CI_PROJECT_DIR/.go
@ -94,7 +68,6 @@ stages:
- mkdir -p .go
cache:
# this caching strategy makes it so each branch uses the same cache
# which I think is the best default strategy
key: "$CI_COMMIT_REF_SLUG"
paths:
- .go/pkg/mod/
@ -114,7 +87,7 @@ golangci-lint:
image: golangci/golangci-lint:v1.46.2
extends: .go-cache
stage: lint
allow_failure: false
allow_failure: true
rules:
- if: '$CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "schedule" || $CI_PIPELINE_SOURCE == "web"'
script:
@ -196,19 +169,52 @@ build featurebase:
- go install github.com/rakyll/statik@v0.1.7
- $GOPATH/bin/statik -src=lattice
- export SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)
- GOOS="linux" GOARCH="amd64" make build FLAGS="-o featurebase_linux_amd64"
- GOOS="linux" GOARCH="arm64" make build FLAGS="-o featurebase_linux_arm64"
- GOOS="darwin" GOARCH="amd64" make build FLAGS="-o featurebase_darwin_amd64"
- GOOS="darwin" GOARCH="arm64" make build FLAGS="-o featurebase_darwin_arm64"
- |
for goos in "darwin" "linux"; do
for goarch in "amd64" "arm64"; do
GOOS="${goos}" GOARCH="${goarch}" make build FLAGS="-o featurebase_${goos}_${goarch}"
done
done
artifacts:
paths:
- featurebase_linux_amd64
- featurebase_linux_arm64
- featurebase_darwin_amd64
- featurebase_darwin_arm64
- featurebase_*
needs:
- job: build lattice
build fbsql amd64:
stage: test
variables:
BUILD_NAME: build_${CI_COMMIT_SHA}_${CI_CONCURRENT_ID}
tags:
- shell
rules:
- if: '$CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "schedule" || $CI_PIPELINE_SOURCE == "web"'
script:
- export SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)
- date
- GOOS="linux" GOARCH="amd64" make docker-build-fbsql BUILD_CGO=1
- GOOS="darwin" GOARCH="amd64" make docker-build-fbsql
artifacts:
paths:
- ./build/fbsql_*
build fbsql arm64:
stage: test
variables:
BUILD_NAME: build_${CI_COMMIT_SHA}_${CI_CONCURRENT_ID}
tags:
- shell-arm64
rules:
- if: '$CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "schedule" || $CI_PIPELINE_SOURCE == "web"'
script:
- export SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)
- date
- GOOS="linux" GOARCH="arm64" make docker-build-fbsql BUILD_CGO=1
- GOOS="darwin" GOARCH="arm64" make docker-build-fbsql
artifacts:
paths:
- ./build/fbsql_*
build amd container fb:
stage: test
tags:
@ -249,13 +255,27 @@ run go tests race:
stage: nonblocking # don't let this job block any other jobs because it takes much longer than the other tests.
image: golang:$GOVERSION
extends: .go-cache
variables:
FEATUREBASE_CONTROLLER_CONFIG_SQLDB_DATABASE: run_go_tests_race
POSTGRES_DB: run_go_tests_race
FEATUREBASE_CONTROLLER_CONFIG_SQLDB_USER: postgres
POSTGRES_USER: postgres
FEATUREBASE_CONTROLLER_CONFIG_SQLDB_PASSWORD: $POSTGRES_PASSWORD
POSTGRES_PASSWORD: $POSTGRES_PASSWORD
FEATUREBASE_CONTROLLER_CONFIG_SQLDB_HOST: postgres
services:
- postgres:14.7
rules:
- if: '$CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "schedule" || $CI_PIPELINE_SOURCE == "web"'
needs: ["smoke build"] # we do block on smoke build though bc it's pretty dumb to test stuff if it doesn't build
script:
- echo "Running featurebase race tests..."
- PKG_LIST=$(go list ./... | grep -Ev 'internal/clustertests|simulacraData|batch|idk|v3/dax/test/dax' | paste -s -d, -)
- RAMDISK=/mnt/ramdisk go test -race -v -timeout=10m ${PKG_LIST//,/ }
- export TMPDIR=/mnt/ramdisk/test-$CI_JOB_ID
- mkdir -p $TMPDIR
- go test -race -v -timeout=10m ${PKG_LIST//,/ }
after_script:
- rm -rf /mnt/ramdisk/test-$CI_JOB_ID
tags:
- docker
@ -266,12 +286,26 @@ run go tests:
stage: test
image: golang:$GOVERSION
extends: .go-cache
variables:
FEATUREBASE_CONTROLLER_CONFIG_SQLDB_DATABASE: run_go_tests
POSTGRES_DB: run_go_tests
FEATUREBASE_CONTROLLER_CONFIG_SQLDB_USER: postgres
POSTGRES_USER: postgres
FEATUREBASE_CONTROLLER_CONFIG_SQLDB_PASSWORD: $POSTGRES_PASSWORD
POSTGRES_PASSWORD: $POSTGRES_PASSWORD
FEATUREBASE_CONTROLLER_CONFIG_SQLDB_HOST: postgres
services:
- postgres:14.7
rules:
- if: '$CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "schedule" || $CI_PIPELINE_SOURCE == "web"'
script:
- echo "Running featurebase unit tests..."
- PKG_LIST=$(go list ./... | grep -Ev 'internal/clustertests|simulacraData|batch|idk|v3/dax/test/dax' | paste -s -d, -)
- RAMDISK=/mnt/ramdisk go test -tags=shardwidth22 -timeout=10m -coverprofile=coverage.out -covermode=atomic -coverpkg=${PKG_LIST} ${PKG_LIST//,/ }
- export TMPDIR=/mnt/ramdisk/test-$CI_JOB_ID
- mkdir -p $TMPDIR
- go test -tags=shardwidth22 -timeout=10m -coverprofile=coverage.out -covermode=atomic -coverpkg=${PKG_LIST} ${PKG_LIST//,/ }
after_script:
- rm -rf /mnt/ramdisk/test-$CI_JOB_ID
artifacts:
paths:
- coverage.out
@ -281,14 +315,29 @@ run go tests:
run go tests dax/test/dax:
stage: test
image: golang:$GOVERSION
extends: .go-cache
tags:
- aws
- docker
variables:
FEATUREBASE_CONTROLLER_CONFIG_SQLDB_DATABASE: run_go_tests_dax
POSTGRES_DB: run_go_tests_dax
FEATUREBASE_CONTROLLER_CONFIG_SQLDB_USER: postgres
POSTGRES_USER: postgres
FEATUREBASE_CONTROLLER_CONFIG_SQLDB_PASSWORD: $POSTGRES_PASSWORD
POSTGRES_PASSWORD: $POSTGRES_PASSWORD
FEATUREBASE_CONTROLLER_CONFIG_SQLDB_HOST: postgres
services:
- postgres:14.7
rules:
- if: '$CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "schedule" || $CI_PIPELINE_SOURCE == "web"'
script:
- echo "Building FB and Datagen docker images for DAX tests"
- PKG_LIST=$(go list ./... | grep -Ev 'internal/clustertests|simulacraData' | paste -s -d, -)
- export TMPDIR=/mnt/ramdisk/test-$CI_JOB_ID
- mkdir -p $TMPDIR
- go test -coverprofile=coverage-dax-integration.out -covermode=atomic -coverpkg=${PKG_LIST} -timeout=20m ./dax/test/dax
after_script:
- rm -rf /mnt/ramdisk/test-$CI_JOB_ID
artifacts:
paths:
- coverage-dax-integration.out
@ -299,14 +348,12 @@ run go tests idk race:
PROJECT: race_${CI_CONCURRENT_ID}
stage: nonblocking
retry: 1
rules:
- !reference [.idk_changed, rules]
script:
- echo "Running test-all-race"
- cd ./idk/
- echo $PROJECT
- echo $CI_COMMIT_REF_SLUG
- BRANCH_NAME=${CI_COMMIT_REF_SLUG} make test-all-race
- BRANCH_NAME=${CI_COMMIT_REF_SLUG} IDK_FEATUREBASE_TAG=${CI_COMMIT_TAG} IDK_FEATUREBASE_HASH=${CI_COMMIT_SHA} make test-all-race
after_script:
- cd ./idk/
- make save-pilosa-logs
@ -326,14 +373,12 @@ run go tests idk shard transactional:
PROJECT: shardttrans_${CI_CONCURRENT_ID}
stage: nonblocking
retry: 1
rules:
- !reference [.idk_changed, rules]
script:
- echo "Running shard transactional tests"
- cd ./idk/
- echo $PROJECT
- echo $CI_COMMIT_REF_SLUG
- BRANCH_NAME=${CI_COMMIT_REF_SLUG} make test-all
- BRANCH_NAME=${CI_COMMIT_REF_SLUG} IDK_FEATUREBASE_TAG=${CI_COMMIT_TAG} IDK_FEATUREBASE_HASH=${CI_COMMIT_SHA} make test-all
after_script:
- cd ./idk/
- make save-pilosa-logs
@ -359,13 +404,11 @@ run go tests idk 533:
- cd ./idk/
- echo $PROJECT
- echo $CI_COMMIT_REF_SLUG
- CONFLUENT_VERSION=5.3.3 BRANCH_NAME=${CI_COMMIT_REF_SLUG} make test-all
- CONFLUENT_VERSION=5.3.3 BRANCH_NAME=${CI_COMMIT_REF_SLUG} IDK_FEATUREBASE_TAG=${CI_COMMIT_TAG} IDK_FEATUREBASE_HASH=${CI_COMMIT_SHA} make test-all
after_script:
- cd ./idk/
- make save-pilosa-logs
- make shutdown
rules:
- !reference [.idk_changed, rules]
tags:
- shell
- aws
@ -385,13 +428,11 @@ run go tests idk sasl:
- cd ./idk/
- echo $PROJECT
- echo $CI_COMMIT_REF_SLUG
- BRANCH_NAME=${CI_COMMIT_REF_SLUG} make test-all-kafka-sasl
- BRANCH_NAME=${CI_COMMIT_REF_SLUG} IDK_FEATUREBASE_TAG=${CI_COMMIT_TAG} IDK_FEATUREBASE_HASH=${CI_COMMIT_SHA} make test-all-kafka-sasl
after_script:
- cd ./idk/
- make save-pilosa-logs
- make shutdown
rules:
- !reference [.idk_changed, rules]
tags:
- shell
- aws
@ -451,7 +492,8 @@ upload_artifacts_to_nexus:
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
script:
- find . -maxdepth 1 -name '*.rpm' -exec curl -v --user "$NEXUS_YUM_CREDS" --upload-file {} https://nexus.molecula.com/repository/molecula-yum/release/ \;
- curl -v --user "$NEXUS_YUM_CREDS" --upload-file *.arm64.rpm https://nexus.molecula.com/repository/molecula-yum/release/
- curl -v --user "$NEXUS_YUM_CREDS" --upload-file *.amd64.rpm https://nexus.molecula.com/repository/molecula-yum/release/
dependencies:
- package for linux amd64
- package for linux arm64
@ -506,8 +548,6 @@ idk build_amd64:
stage: build
variables:
BUILD_NAME: build_${CI_COMMIT_SHA}_${CI_CONCURRENT_ID}
extends:
- .setup_ssh
tags:
- shell
rules:
@ -529,8 +569,6 @@ idk build_amd64:
idk build_arm64:
stage: build
extends:
- .setup_ssh
tags:
- shell-arm64
rules:
@ -554,8 +592,6 @@ idk build_arm64:
# only do containers on default branch
idk package_docker_all:
stage: build
extends:
- .setup_ssh
tags:
- shell
rules:
@ -571,8 +607,6 @@ idk package_docker_all:
idk s3 dump:
stage: post build
extends:
- .setup_ssh
allow_failure: false
variables:
PROFILE: "service-fb-ci"
@ -596,8 +630,6 @@ idk s3 dump:
idk s3 dump tag:
stage: post build
extends:
- .setup_ssh
variables:
PROFILE: "service-fb-ci"
AWS_SSH_PRIVATE_KEY: $AWS_FBCI_SSH_KEY
@ -647,66 +679,6 @@ external lookup tests:
- apt-get install -y postgresql-client
- go test . -run "^TestExternalLookup" -externalLookupDSN postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@postgres/$POSTGRES_DB?sslmode=disable
smoke test:
stage: integration
image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest
variables:
PROFILE: "service-terraform"
AWS_SSH_PRIVATE_KEY: $AWS_FBCI_SSH_KEY
AWS_ACCESS_KEY_ID: $AWS_FBCI_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY: $AWS_FBCI_SECRET_ACCESS_KEY
TF_VAR_cluster_prefix: ""
tags:
- aws
- docker
- fbsmoke
rules:
- if: '$CI_PIPELINE_SOURCE == "push"'
before_script:
- apt-get update && apt-get install -y gnupg software-properties-common curl git
- curl -fsSL https://apt.releases.hashicorp.com/gpg | apt-key add -
- apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"
- apt-get update && apt-get install terraform
- aws configure set aws_access_key_id $AWS_FBCI_ACCESS_KEY_ID --profile $PROFILE
- aws configure set aws_secret_access_key $AWS_FBCI_SECRET_ACCESS_KEY --profile $PROFILE
- aws configure set region "us-east-2" --profile $PROFILE
- aws configure set aws_profile $PROFILE
- echo $AWS_FBCI_SSH_KEY > gitlab-featurebase-ci.pem
- chmod 400 gitlab-featurebase-ci.pem
- "which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )"
- eval $(ssh-agent -s)
- mkdir -p ~/.ssh
- echo $AWS_FBCI_SSH_KEY > /root/.ssh/gitlab-featurebase-ci.pem
- chmod 400 /root/.ssh/gitlab-featurebase-ci.pem
- echo "$AWS_FBCI_SSH_KEY" | ssh-add -
- chmod 700 /root/.ssh
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
- apt update && apt -y install jq wget git libnss3-tools
- wget -q https://go.dev/dl/go$GOVERSION.linux-amd64.tar.gz
- tar -C /usr/local -xzf go$GOVERSION.linux-amd64.tar.gz
- export PATH=$PATH:/usr/local/go/bin
- TF_VAR_cluster_prefix="pipeline-$CI_PIPELINE_ID-smoke-$CI_JOB_ID"
- echo "Cluster Prefix --> $TF_VAR_cluster_prefix"
# download datagen for FB-1270 repro test.
- aws s3 cp s3://molecula-artifact-storage/idk/${CI_COMMIT_BRANCH}/_latest/idk-linux-arm64/datagen ./datagen_linux_arm64
- aws s3 cp s3://molecula-artifact-storage/featurebase/${CI_COMMIT_BRANCH}/_latest/featurebase_linux_arm64 ./
- chmod +x ./datagen_linux_arm64 ./featurebase_linux_arm64
script:
- ./qa/scripts/setupSmokeTest.sh $CI_COMMIT_BRANCH
- ./qa/scripts/testSmokeTest.sh
- ./qa/scripts/bug_repro_tests.sh
after_script:
- ./qa/scripts/teardownSmokeTest.sh
needs:
- job: s3 dump
- job: idk s3 dump
artifacts:
when: always
paths:
- report.xml
reports:
junit: report.xml
s3 dump:
stage: post build
variables:
@ -723,16 +695,19 @@ s3 dump:
- aws configure set aws_secret_access_key $AWS_FBCI_SECRET_ACCESS_KEY
- aws configure set region "us-east-2"
- aws configure set aws_profile $PROFILE
- aws s3 cp featurebase_linux_amd64 s3://molecula-artifact-storage/featurebase/${CI_COMMIT_BRANCH}/${CI_COMMIT_SHORT_SHA}/featurebase_linux_amd64
- aws s3 cp featurebase_linux_amd64 s3://molecula-artifact-storage/featurebase/${CI_COMMIT_BRANCH}/_latest/featurebase_linux_amd64
- aws s3 cp featurebase_linux_arm64 s3://molecula-artifact-storage/featurebase/${CI_COMMIT_BRANCH}/${CI_COMMIT_SHORT_SHA}/featurebase_linux_arm64
- aws s3 cp featurebase_linux_arm64 s3://molecula-artifact-storage/featurebase/${CI_COMMIT_BRANCH}/_latest/featurebase_linux_arm64
- aws s3 cp featurebase_darwin_amd64 s3://molecula-artifact-storage/featurebase/${CI_COMMIT_BRANCH}/${CI_COMMIT_SHORT_SHA}/featurebase_darwin_amd64
- aws s3 cp featurebase_darwin_amd64 s3://molecula-artifact-storage/featurebase/${CI_COMMIT_BRANCH}/_latest/featurebase_darwin_amd64
- aws s3 cp featurebase_darwin_arm64 s3://molecula-artifact-storage/featurebase/${CI_COMMIT_BRANCH}/${CI_COMMIT_SHORT_SHA}/featurebase_darwin_arm64
- aws s3 cp featurebase_darwin_arm64 s3://molecula-artifact-storage/featurebase/${CI_COMMIT_BRANCH}/_latest/featurebase_darwin_arm64
- |
for goos in "darwin" "linux"; do
for goarch in "amd64" "arm64"; do
aws s3 cp featurebase_${goos}_${goarch} s3://molecula-artifact-storage/featurebase/${CI_COMMIT_BRANCH}/${CI_COMMIT_SHORT_SHA}/featurebase_${goos}_${goarch}
aws s3 cp featurebase_${goos}_${goarch} s3://molecula-artifact-storage/featurebase/${CI_COMMIT_BRANCH}/_latest/featurebase_${goos}_${goarch}
aws s3 cp ./build/fbsql_${goos}_${goarch} s3://molecula-artifact-storage/featurebase/${CI_COMMIT_BRANCH}/${CI_COMMIT_SHORT_SHA}/fbsql_${goos}_${goarch}
aws s3 cp ./build/fbsql_${goos}_${goarch} s3://molecula-artifact-storage/featurebase/${CI_COMMIT_BRANCH}/_latest/fbsql_${goos}_${goarch}
done
done
needs:
- job: build featurebase
- job: build fbsql amd64
- job: build fbsql arm64
s3 dump tag:
stage: post build
@ -758,6 +733,7 @@ s3 dump tag:
echo "Directory ${dir}"
mkdir $dir
mv featurebase_${goos}_${goarch} ${dir}/featurebase
mv ./build/fbsql_${goos}_${goarch} ${dir}/fbsql
cp NOTICE install/featurebase.conf install/featurebase.*.service ${dir}/
tar cvzf ${dir}.tar.gz ${dir}
aws s3 cp ${dir} s3://${LOCATION}/${CI_COMMIT_TAG}/${dir}/ --recursive
@ -767,24 +743,5 @@ s3 dump tag:
needs:
- job: build featurebase
cleanup_build_job:
stage: cleanup_build
image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest
variables:
FBCI_PROFILE: "service-terraform"
AWS_ACCESS_KEY_ID: $AWS_FBCI_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY: $AWS_FBCI_SECRET_ACCESS_KEY
tags:
- aws
- docker
- fbsmoke
script:
- aws configure set aws_access_key_id $AWS_FBCI_ACCESS_KEY_ID --profile $FBCI_PROFILE
- aws configure set aws_secret_access_key $AWS_FBCI_SECRET_ACCESS_KEY --profile $FBCI_PROFILE
- aws configure set region "us-east-2" --profile $FBCI_PROFILE
- aws configure set aws_profile $FBCI_PROFILE
- ./qa/scripts/gitlabCleanupBuild.sh
when: always
needs:
- job: smoke test
- job: build fbsql amd64
- job: build fbsql arm64

View file

@ -1,64 +0,0 @@
variables:
GOVERSION: "1.19"
stages:
- performance
perf_able:
stage: performance
timeout: 2h
image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest
variables:
PROFILE: "service-terraform"
INFRA_PROFILE: "service-gitlab"
AWS_SSH_PRIVATE_KEY: $AWS_FBCI_SSH_KEY
AWS_ACCESS_KEY_ID: $AWS_FBCI_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY: $AWS_FBCI_SECRET_ACCESS_KEY
ASG_NAME: "gitlab-runners"
TF_VAR_cluster_prefix: ""
tags:
- aws
- docker
- fbsmoke
before_script:
- apt-get update && apt-get install -y gnupg software-properties-common curl git
- curl -fsSL https://apt.releases.hashicorp.com/gpg | apt-key add -
- apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"
- apt-get update && apt-get install terraform
- aws configure set aws_access_key_id $AWS_FBCI_ACCESS_KEY_ID --profile $PROFILE
- aws configure set aws_secret_access_key $AWS_FBCI_SECRET_ACCESS_KEY --profile $PROFILE
- aws configure set region "us-east-2" --profile $PROFILE
- aws configure set aws_profile $PROFILE
- aws configure set aws_access_key_id $AWS_INFRA_ACCESS_KEY_ID --profile $INFRA_PROFILE
- aws configure set aws_secret_access_key $AWS_INFRA_SECRET_ACCESS_KEY --profile $INFRA_PROFILE
- aws configure set region "us-east-2" --profile $INFRA_PROFILE
- echo $AWS_FBCI_SSH_KEY > gitlab-featurebase-ci.pem
- chmod 400 gitlab-featurebase-ci.pem
- "which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )"
- eval $(ssh-agent -s)
- mkdir -p ~/.ssh
- echo $AWS_FBCI_SSH_KEY > /root/.ssh/gitlab-featurebase-ci.pem
- chmod 400 /root/.ssh/gitlab-featurebase-ci.pem
- echo "$AWS_FBCI_SSH_KEY" | ssh-add -
- chmod 700 /root/.ssh
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
- apt update && apt -y install jq wget
- wget -q https://go.dev/dl/go$GOVERSION.linux-amd64.tar.gz
- tar -C /usr/local -xzf go$GOVERSION.linux-amd64.tar.gz
- export PATH=$PATH:/usr/local/go/bin
- TF_VAR_cluster_prefix="able-$(openssl rand -base64 12 | tr -d /=+ | cut -c -16)"
- echo "Cluster Prefix --> $TF_VAR_cluster_prefix"
- export INSTANCE_ID=$(curl --silent --fail "http://169.254.169.254/latest/meta-data/instance-id" | tee instance_id)
- aws autoscaling set-instance-protection --instance-ids "$INSTANCE_ID" --auto-scaling-group-name $ASG_NAME --protected-from-scale-in --profile $INFRA_PROFILE
- aws s3 cp s3://molecula-artifact-storage/featurebase/${CI_COMMIT_BRANCH}/_latest/featurebase_linux_arm64 ./
- chmod +x ./featurebase_linux_arm64
script:
- ./qa/scripts/perf/able/ableSetup.sh $CI_COMMIT_BRANCH
- ./qa/scripts/perf/able/ableTest.sh
after_script:
- ./qa/scripts/perf/able/ableTeardown.sh || true
- export INSTANCE_ID=$(cat instance_id)
- aws autoscaling set-instance-protection --instance-ids "$INSTANCE_ID" --auto-scaling-group-name $ASG_NAME --no-protected-from-scale-in --profile $INFRA_PROFILE
needs:
- pipeline: $PARENT_PIPELINE_ID
job: build featurebase

View file

@ -1,8 +1,8 @@
run:
#skip the protobuf generated files
deadline: 5m
timeout: 5m
skip-dirs-use-default: true
#skip the protobuf generated files
skip-dirs:
- pb
- proto
@ -10,9 +10,26 @@ run:
- pql/pql.peg.go
linters:
enable:
# Recommended to be enabled by default (https://golangci-lint.run).
# - errcheck (lots to fix)
- gosimple
- govet
- gofmt
- ineffassign
- staticcheck
- typecheck
# - unused (about 20 to fix)
# Additional linters we choose to enable.
# - bodyclose (lots to fix, but we should)
- errchkjson
- errname
- gofmt
# - misspell (lots to fix, but we should)
- prealloc
# - predeclared (20 to fix)
# - stylecheck (quite a lot to fix, but we should definitely work on this)
- stylecheck
# - unconvert (not at all critical, but makes for cleaner code)
enable-all: false
disable-all: true
@ -50,6 +67,12 @@ linters-settings:
- shadow
disable-all: false
stylecheck:
# ST1000: at least one file in a package should have a package comment
# ST1003: golang naming standards
# ST1016: methods on the same type should have the same receiver name
# ST1020: comment on exported function
checks: ["all", "-ST1000", "-ST1003", "-ST1016", "-ST1020"]
issues:
exclude-use-default: false
@ -58,4 +81,3 @@ issues:
exclude:
- 'declaration of "(err|ctx)" shadows declaration at'
- 'Error return value of .(.*\.Help|.*\.MarkFlagRequired|(os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*printf?|os\.(Un)?Setenv). is not checked'

View file

@ -5,7 +5,7 @@ FROM golang:1.19
LABEL maintainer "dev@pilosa.com"
COPY . /go/src/github.com/molecula/featurebase/
COPY . /go/src/github.com/featurebasedb/featurebase/
# download pumba for fault injection
@ -20,12 +20,12 @@ RUN apt install -y docker.io
ADD https://github.com/docker/compose/releases/latest/download/docker-compose-Linux-x86_64 /usr/local/bin/docker-compose
RUN chmod +x /usr/local/bin/docker-compose
WORKDIR /go/src/github.com/molecula/featurebase/cmd/featurebase
WORKDIR /go/src/github.com/featurebasedb/featurebase/cmd/featurebase
# generate an instrumented binary to allow for calculating code coverage for clustertests
# the entrypoint for the binary is TestRunMain, which is wrapper for main
RUN go test -covermode=atomic -coverpkg=../../... -c -tags testrunmain -o featurebase
RUN cp /go/src/github.com/molecula/featurebase/cmd/featurebase/featurebase /featurebase
RUN cp /go/src/github.com/featurebasedb/featurebase/cmd/featurebase/featurebase /featurebase
COPY NOTICE /NOTICE

View file

@ -5,7 +5,7 @@ FROM golang:1.19
LABEL maintainer "dev@pilosa.com"
COPY . /go/src/github.com/molecula/featurebase/
COPY . /go/src/github.com/featurebasedb/featurebase/
# download pumba for fault injection
ADD https://github.com/alexei-led/pumba/releases/download/0.6.0/pumba_linux_amd64 /pumba
@ -19,19 +19,19 @@ RUN apt install -y docker.io
ADD https://github.com/docker/compose/releases/latest/download/docker-compose-Linux-x86_64 /usr/local/bin/docker-compose
RUN chmod +x /usr/local/bin/docker-compose
WORKDIR /go/src/github.com/molecula/featurebase/cmd/featurebase
WORKDIR /go/src/github.com/featurebasedb/featurebase/cmd/featurebase
RUN go test -covermode=atomic -coverpkg=../../... -c -tags testrunmain -o featurebase
RUN cp /go/src/github.com/molecula/featurebase/cmd/featurebase/featurebase /featurebase
RUN cp /go/src/github.com/featurebasedb/featurebase/cmd/featurebase/featurebase /featurebase
COPY NOTICE /NOTICE
COPY ./internal/clustertests /go/src/github.com/molecula/featurebase/internal/clustertests
COPY ./internal/clustertests /go/src/github.com/featurebasedb/featurebase/internal/clustertests
EXPOSE 10101
VOLUME /data
WORKDIR /go/src/github.com/molecula/featurebase
WORKDIR /go/src/github.com/featurebasedb/featurebase
CMD ["/featurebase", "-test.run=TestRunMain", "-test.coverprofile=/results/coverage.out", "server", "--data-dir", "/data", "--bind", "http://0.0.0.0:10101"]

View file

@ -16,7 +16,7 @@ RUN make build FLAGS="-o build/featurebase" ${MAKE_FLAGS}
### FeatureBase runner ###
##########################
FROM alpine:3.13.2 as runner
FROM golang:alpine as runner
LABEL maintainer "dev@featurebase.com"

48
Dockerfile-fbsql Normal file
View file

@ -0,0 +1,48 @@
ARG GO_VERSION=1.19
FROM golang:1.19-buster as builder
WORKDIR /
RUN apt-get update -y -qq && apt-get install -y -qq \
build-essential \
git \
musl-tools \
netcat \
unixodbc \
unixodbc-dev \
&& rm -rf /var/lib/apt/lists/*
RUN ["git", "clone", "https://github.com/edenhill/librdkafka.git"]
WORKDIR /librdkafka
RUN ./configure --prefix /usr && \
make && \
make install
WORKDIR /featurebase
COPY . .
ARG MAKE_FLAGS
ARG GO_BUILD_FLAGS
ARG SOURCE_DATE_EPOCH
WORKDIR /featurebase/
ENV SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH}
RUN make build-fbsql GO_BUILD_FLAGS="-mod=vendor ${GO_BUILD_FLAGS}" ${MAKE_FLAGS}
FROM ubuntu:20.04 as runner
RUN apt-get update -y -qq && apt-get install -y -qq \
ca-certificates \
musl-tools \
netcat \
unixodbc-dev \
&& rm -rf /var/lib/apt/lists/*
COPY --from=builder /featurebase/fbsql /usr/local/bin/
# Verify that the linker can find everything.
FROM runner as linkcheck
RUN if [ -e /usr/local/bin/fbsql ] ; then ldd /usr/local/bin/fbsql; fi
FROM runner

201
LICENSE Normal file
View file

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2023 Molecula Corp. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

202
LICENSE-2.0.txt Normal file
View file

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

114
Makefile
View file

@ -1,4 +1,4 @@
.PHONY: build clean build-lattice cover cover-viz default docker docker-build docker-tag-push generate generate-protoc generate-pql generate-statik generate-stringer install install-protoc-gen-gofast install-protoc install-statik install-peg test docker-login
.PHONY: build clean build-lattice cover cover-viz default docker docker-build docker-build-fbsql docker-tag-push generate generate-protoc generate-pql generate-statik generate-stringer install install-protoc-gen-gofast install-protoc install-statik install-peg test docker-login
SHELL := /bin/bash
VERSION := $(shell git describe --tags 2> /dev/null || echo unknown)
@ -17,12 +17,17 @@ else
endif
SHARD_WIDTH = 20
COMMIT := $(shell git describe --exact-match >/dev/null 2>&1 || git rev-parse --short HEAD)
LDFLAGS="-X github.com/molecula/featurebase/v3.Version=$(VERSION) -X github.com/molecula/featurebase/v3.BuildTime=$(BUILD_TIME) -X github.com/molecula/featurebase/v3.Variant=$(VARIANT) -X github.com/molecula/featurebase/v3.Commit=$(COMMIT) -X github.com/molecula/featurebase/v3.TrialDeadline=$(TRIAL_DEADLINE)"
LDFLAGS="-X github.com/featurebasedb/featurebase/v3.Version=$(VERSION) -X github.com/featurebasedb/featurebase/v3.BuildTime=$(BUILD_TIME) -X github.com/featurebasedb/featurebase/v3.Variant=$(VARIANT) -X github.com/featurebasedb/featurebase/v3.Commit=$(COMMIT) -X github.com/featurebasedb/featurebase/v3.TrialDeadline=$(TRIAL_DEADLINE)"
GO_VERSION=1.19
BUILD_TAGS += shardwidth$(SHARD_WIDTH)
GO_BUILD_FLAGS=
DOCKER_BUILD= # set to 1 to use `docker-build` instead of `build` when creating a release
BUILD_TAGS +=
TEST_TAGS = roaringparanoia
TEST_TIMEOUT=10m
RACE_TEST_TIMEOUT=10m
# size in GB to use for ramdisk, ?= so you can override it with env
# 4GB is not enough for `make test`, 8GB usually is.
RAMDISK_SIZE ?= 8
export GO111MODULE=on
export GOPRIVATE=github.com/molecula
@ -46,11 +51,11 @@ version:
# We build a list of packages that omits the IDK and batch packages because
# those packages require fancy environment setup.
GOPACKAGES := $(shell $(GO) list ./... | grep -v "/idk" | grep -v "/batch")
GOPACKAGES := $(shell $(GO) list ./... | grep -v "/v3/idk" | grep -v "/v3/batch")
# Run test suite
test:
$(GO) test $(GOPACKAGES) -tags='$(BUILD_TAGS) $(TEST_TAGS)' $(TESTFLAGS) -v -timeout $(TEST_TIMEOUT)
$(GO) test $(GOPACKAGES) -tags='$(BUILD_TAGS) $(TEST_TAGS)' $(TESTFLAGS) -v -timeout $(TEST_TIMEOUT) -count=1
# Run test suite with race flag
test-race:
@ -67,7 +72,7 @@ testv-race: testvsub-race
#
testvsub:
@set -e; for pkg in $(GOPACKAGES); do \
if [ $${pkg:0:38} == "github.com/molecula/featurebase/v3/idk" ]; then \
if [ $${pkg:0:38} == "github.com/featurebasedb/featurebase/v3/idk" ]; then \
echo; echo "___ skipping subpkg $$pkg"; \
continue; \
fi; \
@ -76,13 +81,18 @@ testvsub:
echo; echo "999 done testing subpkg $$pkg"; \
done
# make a 2GB RAMDisk. Speed up tests by running them with RAMDISK=/mnt/ramdisk
# make a $(RAMDISK_SIZE)GB RAMDisk. Speed up tests by running
# them with TMPDIR=/mnt/ramdisk.
ramdisk-linux:
mount -o size=2G -t tmpfs none /mnt/ramdisk
mount -o size=$(RAMDISK__SIZE)G -t tmpfs none /mnt/ramdisk
# make a 2GB RAMDisk. Speed up tests by running them with RAMDISK=/Volumes/RAMDisk
# make a $(RAMDISK_SIZE)GB RAMDisk. Speed up tests by running
# them with TMPDIR=/Volumes/RAMDisk. This is more important on
# OS X than it is on Linux, because there's performance issues
# with fsync on OS X that can make the SSD slow down to moving-platters
# drive speeds. Oops.
ramdisk-osx:
diskutil erasevolume HFS+ 'RAMDisk' `hdiutil attach -nobrowse -nomount ram://4194304`
diskutil erasevolume HFS+ 'RAMDisk' $$(hdiutil attach -nobrowse -nomount ram://$$(expr 2097152 \* $(RAMDISK_SIZE)))
detach-ramdisk-osx:
hdiutil detach /Volumes/RAMDisk
@ -107,12 +117,13 @@ cover:
cover-viz: cover
$(GO) tool cover -html=build/coverage.out
# Compile Pilosa
# Build featurebase
build:
$(GO) build -tags='$(BUILD_TAGS)' -ldflags $(LDFLAGS) $(FLAGS) ./cmd/featurebase
package:
GOOS=$(GOOS) GOARCH=$(GOARCH) FLAGS="-o featurebase" $(MAKE) build
GOOS=$(GOOS) GOARCH=$(GOARCH) $(MAKE) build
GOOS=$(GOOS) GOARCH=$(GOARCH) $(MAKE) build-fbsql
GOARCH=$(GOARCH) VERSION=$(VERSION) nfpm package --packager deb --target featurebase.$(VERSION).$(GOARCH).deb
GOARCH=$(GOARCH) VERSION=$(VERSION) nfpm package --packager rpm --target featurebase.$(VERSION).$(GOARCH).rpm
@ -134,7 +145,7 @@ clustertests: vendor
$(DOCKER_COMPOSE) -f internal/clustertests/docker-compose.yml down
# Run the cluster tests with authentication enabled
AUTH_ARGS="-c /go/src/github.com/molecula/featurebase/internal/clustertests/testdata/featurebase.conf"
AUTH_ARGS="-c /go/src/github.com/featurebasedb/featurebase/internal/clustertests/testdata/featurebase.conf"
authclustertests: vendor
CLUSTERTESTS_FB_ARGS=$(AUTH_ARGS) $(DOCKER_COMPOSE) -f internal/clustertests/docker-compose.yml down
CLUSTERTESTS_FB_ARGS=$(AUTH_ARGS) $(DOCKER_COMPOSE) -f internal/clustertests/docker-compose.yml build
@ -143,7 +154,7 @@ authclustertests: vendor
CLUSTERTESTS_FB_ARGS=$(AUTH_ARGS) $(DOCKER_COMPOSE) -f internal/clustertests/docker-compose.yml down
# Install FeatureBase and IDK
install: install-featurebase install-idk
install: install-featurebase install-idk install-fbsql
install-featurebase:
$(GO) install -tags='$(BUILD_TAGS)' -ldflags $(LDFLAGS) $(FLAGS) ./cmd/featurebase
@ -151,6 +162,9 @@ install-featurebase:
install-idk:
$(MAKE) -C ./idk install
install-fbsql:
CGO_ENABLED=1 $(GO) install ./cmd/fbsql
# Build the lattice assets
build-lattice:
docker build -t lattice:build ./lattice
@ -158,16 +172,20 @@ build-lattice:
# `go generate` protocol buffers
generate-protoc: require-protoc require-protoc-gen-gofast
$(GO) generate github.com/molecula/featurebase/v3/pb
$(GO) generate github.com/featurebasedb/featurebase/v3/pb
# `go generate` statik assets (lattice UI)
generate-statik: build-lattice require-statik
$(GO) generate github.com/molecula/featurebase/v3/statik
$(GO) generate github.com/featurebasedb/featurebase/v3/statik
# `go generate` statik assets (lattice UI) in Docker
generate-statik-docker: build-lattice
docker run --rm -t -v $(PWD):/pilosa golang:1.15.8 sh -c "go get github.com/rakyll/statik && /go/bin/statik -src=/pilosa/lattice/build -dest=/pilosa -f"
# `go generate` stringers
generate-stringer:
$(GO) generate github.com/featurebasedb/featurebase/v3
generate-pql: require-peg
cd pql && peg -inline pql.peg && cd ..
@ -215,6 +233,13 @@ docker-image-featurebase: vendor
--file Dockerfile-dax \
--tag dax/featurebase .
docker-image-featurebase-linux-amd64: vendor
docker build \
--build-arg GO_VERSION=$(GO_VERSION) \
--platform linux/amd64 \
--file Dockerfile-dax \
--tag dax/featurebase .
docker-image-featurebase-test: vendor
docker build \
--build-arg GO_VERSION=$(GO_VERSION) \
@ -241,7 +266,12 @@ docker-image-featurebase-quick: build-for-quick
docker-image-datagen: vendor
docker build --tag dax/datagen --file Dockerfile-datagen .
get-account-id:
$(eval AWS_ACCOUNTID := $(shell aws sts get-caller-identity --output=json | jq -r .Account))
ecr-push-featurebase: docker-login
echo "Pushing to account $(AWS_ACCOUNTID), profile $(AWS_PROFILE)"
docker tag dax/featurebase:latest $(AWS_ACCOUNTID).dkr.ecr.us-east-2.amazonaws.com/dax/featurebase:latest
docker push $(AWS_ACCOUNTID).dkr.ecr.us-east-2.amazonaws.com/dax/featurebase:latest
@ -249,7 +279,7 @@ ecr-push-datagen: docker-login
docker tag dax/datagen:latest $(AWS_ACCOUNTID).dkr.ecr.us-east-2.amazonaws.com/dax/datagen:latest
docker push $(AWS_ACCOUNTID).dkr.ecr.us-east-2.amazonaws.com/dax/datagen:latest
docker-login:
docker-login: get-account-id
aws ecr get-login-password --region us-east-2 | docker login --username AWS --password-stdin $(AWS_ACCOUNTID).dkr.ecr.us-east-2.amazonaws.com
# Create docker image (alias)
@ -323,3 +353,53 @@ test-external-lookup:
bnf:
ebnf2railroad --no-overview-diagram --no-optimizations ./sql3/sql3.ebnf
#################################
# fbsql builds in docker
#################################
# This allows multiple concurrent builds to happen in CI without
# creating container name conflicts and such. (different BUILD_NAMEs
# are passed in from gitlab-ci.yml)
BUILD_NAME ?= fbsql-build
LDFLAGS_STATIC="-linkmode external -extldflags \"-static\" -X 'github.com/featurebasedb/featurebase/v3/fbsql.Version=$(VERSION)' -X 'github.com/featurebasedb/featurebase/v3/fbsql.BuildTime=$(BUILD_TIME)' "
UNAME_P := $(shell uname -p)
BUILD_CGO ?= 0
# Build fbsql
build-fbsql:
@echo GOOS=$(GOOS) GOARCH=$(GOARCH) uname -p=$(UNAME_P) build_cgo=$(BUILD_CGO)
ifeq ($(BUILD_CGO), 0)
make build-fbsql-non-cgo
endif
ifeq ($(BUILD_CGO), 1)
make build-fbsql-cgo
endif
build-fbsql-non-cgo:
CGO_ENABLED=0 $(GO) build -ldflags $(LDFLAGS) $(GO_BUILD_FLAGS) -o fbsql ./cmd/fbsql
build-fbsql-cgo:
ifeq ($(GOARCH), arm64)
CGO_ENABLED=1 $(GO) build -tags dynamic $(GO_BUILD_FLAGS) -o fbsql ./cmd/fbsql
endif
ifeq ($(GOARCH), amd64)
CC=/usr/bin/musl-gcc CGO_ENABLED=1 $(GO) build -tags "musl static" -ldflags $(LDFLAGS_STATIC) $(GO_BUILD_FLAGS) -o fbsql ./cmd/fbsql
endif
docker-build-fbsql: vendor
DOCKER_BUILDKIT=0 docker build \
--file Dockerfile-fbsql \
--build-arg GO_VERSION=$(GO_VERSION) \
--build-arg MAKE_FLAGS="GOOS=$(GOOS) GOARCH=$(GOARCH) BUILD_CGO=$(BUILD_CGO)" \
--build-arg GO_BUILD_FLAGS=$(GO_BUILD_FLAGS) \
--build-arg SOURCE_DATE_EPOCH=$(SOURCE_DATE_EPOCH) \
--target builder \
--tag fbsql:$(BUILD_NAME) .
mkdir -p build
docker create --name $(BUILD_NAME) fbsql:$(BUILD_NAME)
docker cp $(BUILD_NAME):/featurebase/fbsql ./build/fbsql_$(GOOS)_$(GOARCH)
docker rm $(BUILD_NAME)

45
OPENSOURCE.md Normal file
View file

@ -0,0 +1,45 @@
## User Contribution Guidelines for FeatureBase
Thank you for your interest in contributing to FeatureBase! We appreciate your support in making this open-source project even better. Here are some guidelines to help you get started with contributing to FeatureBase:
1. Familiarize Yourself with the Project:
- Visit the FeatureBase website at www.featurebase.com to understand the project's goals, capabilities, and features.
- Read the documentation available on the website, including the installation guide, configuration options, and data modeling concepts.
- Explore the codebase by cloning the repository and reviewing the source code.
2. Join the Community:
- Visit the FeatureBase community page at https://www.featurebase.com/community to learn more about the project's community and how to get involved.
- Join the Discord server at https://discord.gg/FBn2vEp7Na to chat with other contributors and users, ask questions, and share your ideas.
3. Set Up Your Development Environment:
- Ensure you have Go installed on your machine. Make sure your shell's search path includes the go/bin directory.
- Clone the FeatureBase repository or download it as a zip file from the repository's page.
- Follow the "Build FeatureBase Server from source" instructions in the README file to compile the server binary and the ingester binaries.
4. Choose a Contribution Area:
- Identify the area you'd like to contribute to, such as bug fixes, new features, performance improvements, documentation updates, or community support.
- Check the issue tracker on the repository or the FeatureBase community for open issues or feature requests that align with your interests and skills. Alternatively, propose your own idea by creating a new issue.
5. Create a New Branch:
- Before making any changes, create a new branch in the repository's Git repository. This branch will contain your contributions.
- Give your branch a descriptive name that reflects the nature of your contribution.
6. Make Your Changes:
- Follow the coding style and conventions used in the existing codebase.
- Write clear and concise commit messages for each logical change.
- If you're introducing new features or modifying existing behavior, make sure to update the documentation to reflect the changes.
7. Test Your Changes:
- Run the existing test suite to ensure that your modifications do not introduce any regressions.
- If applicable, write additional tests to cover the changes you made.
- Document any new testing procedures required for your contribution.
8. Submitting Your Contribution:
- Push your branch to the main repository or create a fork and submit a pull request to the main repository.
- Provide a detailed description of your changes, including the problem you solved and the approach you took.
- Be responsive to any feedback or suggestions provided by the project maintainers or other contributors.
- Once your contribution is approved, it will be reviewed and merged into the main codebase.
Please note that by contributing to FeatureBase, you agree that your contributions will be licensed under the Apache 2.0 license, which governs the project.
Thank you for considering contributing to FeatureBase! Your contributions are valuable and help improve the project for everyone.

View file

@ -1,11 +1,72 @@
# FeatureBase, a distributed bitmap index
# FeatureBase Community
[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](code_of_conduct.md)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=molecula_featurebase&metric=coverage&token=8e09e593b40570b544ed7defb47018add4eb9e7b)](https://sonarcloud.io/summary/new_code?id=molecula_featurebase)
[![SecurityRating](https://sonarcloud.io/api/project_badges/measure?project=molecula_featurebase&metric=security_rating&token=8e09e593b40570b544ed7defb47018add4eb9e7b)](https://sonarcloud.io/summary/new_code?id=molecula_featurebase)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=molecula_featurebase&metric=alert_status&token=8e09e593b40570b544ed7defb47018add4eb9e7b)](https://sonarcloud.io/summary/new_code?id=molecula_featurebase)
FeatureBase Community is now archived and no longer maintained.
See our [internal documentation](https://internal-docs.molecula.cloud), which includes all [external documentation](https://docs.molecula.cloud), plus many internal-only pages, listed under the "Internal" heading in the main navigation bar.
* [FeatureBase Community Help](https://github.com/FeatureBaseDB/FB-community-help)
Follow along with the [Sample Project](https://internal-docs.molecula.cloud/tutorials/getting-started) to get a better understanding of FeatureBase's capabilities.
## Pilosa is now FeatureBase
As of September 7, 2022, the Pilosa project is now FeatureBase. The core of the project remains the same: FeatureBase is the first real-time distributed database built entirely on bitmaps. (More information about updated capabilities and improvements below.)
FeatureBase delivers low-latency query results, regardless of throughput or query volumes, on fresh data with extreme efficiency. It works because bitmaps are faster, simpler, and far more I/O efficient than traditional column-oriented data formats. With FeatureBase, you can ingest data from batch data sources (e.g. S3, CSV, Snowflake, BigQuery, etc.) and/or streaming data sources (e.g. Kafka/Confluent, Kinesis, Pulsar).
For more information about FeatureBase, please visit [www.featurebase.com][HomePage].
## Getting Started
* [Learn how to install FeatureBase Community](https://github.com/FeatureBaseDB/FB-community-help/blob/main/docs/community/com-getstart/com-getstart-home.md)
### Build FeatureBase Server from source
0. Install go. Ensure that your shell's search path includes the go/bin directory.
1. Clone the FeatureBase repository (or download as zip).
2. In the featurebase directory, run `make install` to compile the FeatureBase server binary. By default, it will be installed in the go/bin directory.
3. In the idk directory, run `make install` to compile the ingester binaries. By default, they will be installed in the go/bin directory.
4. Run `featurebase server --handler.allowed-origins=http://localhost:3000` to run FeatureBase server with default settings (learn more about configuring FeatureBase at the link below). The `--handler.allowed-origins` parameter allows the standalone web UI to talk to the server; this can be omitted if the web UI is not needed.
5. Run `curl localhost:10101/status` to verify the server is running and accessible.
### Data Model
Because FeatureBase is built on bitmaps, there is bit of a learning curve to grasp how your data is represented.
* [Learn about Data Modeling](https://github.com/FeatureBaseDB/FB-community-help/blob/main/docs/concepts/concepts-home.md)
### Ingest Data and Query
* [Learn how to ingest data from multiple data sources](https://github.com/FeatureBaseDB/FB-community-help/blob/main/docs/community/com-ingest/com-ingest-manage.md)
## Community
You can email us at community@featurebase.com and [learn more about contributing](https://github.com/FeatureBaseDB/featurebase/blob/master/OPENSOURCE.md).
Chat with us: [https://discord.gg/FBn2vEp7Na][Discord]
## What's Changed Since the Pilosa Days?
A lot has changed since the days of Pilosa. This list highlights some new capabilites included in FeatureBase. We have also made signficant improvements to the performance, scalability, and stability of the FeatureBase product.
* Query Languages: FeatureBase supports Pilosa Query Language (PQL), as well as SQL
* Stream and Batch Ingest: Combine real-time data streams with batch historical data and act on it within milliseconds.
* Mutable: Perform inserts, updates, and deletes at scale, in real time and on-the-fly. This is key for meeting data compliance requirements, and for reflecting the constantly-changing nature of high-volume data.
* Multi-Valued Set Fields: Store multiple comma-delimited values within a single field while *increasing* query performance of counts, TopKs, etc.
* Time Quantums: Setting a time quantum on a field creates extra views which allow ranged Row queries down to the time interval specified. For example, if the time quantum is set to YMD, ranged Row queries down to the granularity of a day are supported.
* RBF storage backend: this is a new compressed bitmap format which improves performance in a number of ways: ACID support on a per shard basis, prevents issues with the number of open files, reduces memory allocation and lock contention for reads, provides more consistent garbage collection, and allows backups to run concurrently with writes. However, because of this change, Pilosa backup files cannot be restored into FeatureBase.
## License
FeatureBase is licensed under the [Apache License, Version 2.0][License]
[Community]: https://github.com/FeatureBaseDB/FB-community-help/tree/main
[Install]:https://github.com/FeatureBaseDB/FB-community-help/blob/main/docs/community/com-getstart/com-getstart-home.md
[Config]: https://github.com/FeatureBaseDB/FB-community-help/tree/main/docs/community/com-config
[DataModel]: https://github.com/FeatureBaseDB/FB-community-help/blob/main/docs/concepts/concepts-home.md
[Discord]: https://discord.gg/FBn2vEp7Na
[HomePage]: http://featurebase.com?utm_campaign=Open%20Source&utm_source=GitHub
[Ingest]: https://github.com/FeatureBaseDB/FB-community-help/blob/main/docs/community/com-ingest/com-ingest-manage.md
[License]: http://www.apache.org/licenses/LICENSE-2.0
[PQL]: https://docs.featurebase.com/docs/pql-guide/pql-home/?utm_campaign=Open%20Source&utm_source=GitHub
[SQL]: https://docs.featurebase.com/docs/sql-guide/sql-guide-home/?utm_campaign=Open%20Source&utm_source=GitHub

166
api.go
View file

@ -1,4 +1,5 @@
// Copyright 2021 Molecula Corp. All rights reserved.
// Copyright 2022 Molecula Corp. (DBA FeatureBase).
// SPDX-License-Identifier: Apache-2.0
//go:generate stringer -type=apiMethod
package pilosa
@ -21,20 +22,20 @@ import (
"sync"
"time"
fbcontext "github.com/molecula/featurebase/v3/context"
"github.com/molecula/featurebase/v3/dax"
"github.com/molecula/featurebase/v3/dax/computer"
"github.com/molecula/featurebase/v3/dax/storage"
"github.com/molecula/featurebase/v3/disco"
"github.com/molecula/featurebase/v3/logger"
"github.com/molecula/featurebase/v3/rbf"
fbcontext "github.com/featurebasedb/featurebase/v3/context"
"github.com/featurebasedb/featurebase/v3/dax"
"github.com/featurebasedb/featurebase/v3/dax/computer"
"github.com/featurebasedb/featurebase/v3/dax/storage"
"github.com/featurebasedb/featurebase/v3/disco"
"github.com/featurebasedb/featurebase/v3/logger"
"github.com/featurebasedb/featurebase/v3/rbf"
"github.com/prometheus/client_golang/prometheus"
//"github.com/molecula/featurebase/v3/pg"
"github.com/molecula/featurebase/v3/pql"
"github.com/molecula/featurebase/v3/roaring"
planner_types "github.com/molecula/featurebase/v3/sql3/planner/types"
"github.com/molecula/featurebase/v3/tracing"
//"github.com/featurebasedb/featurebase/v3/pg"
"github.com/featurebasedb/featurebase/v3/pql"
"github.com/featurebasedb/featurebase/v3/roaring"
planner_types "github.com/featurebasedb/featurebase/v3/sql3/planner/types"
"github.com/featurebasedb/featurebase/v3/tracing"
"github.com/pkg/errors"
"golang.org/x/sync/errgroup"
)
@ -337,6 +338,14 @@ func (api *API) DeleteIndex(ctx context.Context, indexName string) error {
if err != nil {
return errors.Wrap(err, "deleting index")
}
// Remove from writelogger/snapshotter if serverless.
if api.isComputeNode {
if err := api.serverlessStorage.RemoveTable(dax.TableKey(indexName).QualifiedTableID()); err != nil {
return errors.Wrapf(err, "removing table from serverless storage: %s", indexName)
}
}
// Send the delete index message to all nodes.
err = api.server.SendSync(
&DeleteIndexMessage{
@ -358,8 +367,8 @@ func (api *API) DeleteIndex(ctx context.Context, indexName string) error {
}
// CreateField makes the named field in the named index with the given options.
// This method currently only takes a single functional option, but that may be
// changed in the future to support multiple options.
//
// The resulting field will always have TrackExistence set.
func (api *API) CreateField(ctx context.Context, indexName string, fieldName string, opts ...FieldOption) (*Field, error) {
span, _ := tracing.StartSpanFromContext(ctx, "API.CreateField")
defer span.Finish()
@ -372,6 +381,11 @@ func (api *API) CreateField(ctx context.Context, indexName string, fieldName str
// authN/Z info
requestUserID, _ := fbcontext.UserID(ctx) // requestUserID is "" if not in ctx
// newFieldOptions is also used in the path through the index creating
// a field from an update from DAX, so it can't assume it can always
// override this. But we're the call path for creating new fields, and
// new fields should always have TrackExistence on.
opts = append(opts, OptFieldTrackExistence())
// Apply and validate functional options.
fo, err := newFieldOptions(opts...)
if err != nil {
@ -485,16 +499,9 @@ func importWorker(importWork chan importJob) {
for j := range importWork {
err := func() (err0 error) {
for viewName, viewData := range j.req.Views {
// The logic here corresponds to the logic in fragment.cleanViewName().
// Unfortunately, the logic in that method is not completely exclusive
// (i.e. an "other" view named with format YYYYMMDD would be handled
// incorrectly). One way to address this would be to change the logic
// overall so there weren't conflicts. For now, we just
// rely on the field type to inform the intended view name.
if viewName == "" {
viewName = viewStandard
} else if j.field.Type() == FieldTypeTime {
viewName = fmt.Sprintf("%s_%s", viewStandard, viewName)
viewName, err0 = j.field.cleanupViewName(viewName)
if err0 != nil {
return err0
}
if len(viewData) == 0 {
return fmt.Errorf("no data to import for view: %s", viewName)
@ -1307,7 +1314,6 @@ type ImportOptions struct {
Clear bool
IgnoreKeyCheck bool
Presorted bool
fullySorted bool // format-aware sorting, internal use only please.
suppressLog bool
// test Tx atomicity if > 0
@ -1514,7 +1520,6 @@ func (api *API) ImportWithTx(ctx context.Context, qcx *Qcx, req *ImportRequest,
return errors.Wrap(err, "validating api method")
}
api.server.logger.Debugf("ImportWithTx: %v %v %v", req.Index, req.Field, req.Shard)
idx, field, err := api.indexField(req.Index, req.Field, req.Shard)
if err != nil {
return errors.Wrap(err, "getting index and field")
@ -1633,6 +1638,12 @@ func (api *API) ImportWithTx(ctx context.Context, qcx *Qcx, req *ImportRequest,
// across many fields in a single shard. It can both set and clear
// bits and updates caches/bitDepth as appropriate, although only the
// bitmap parts happen truly transactionally.
//
// This function does not attempt to do existence tracking, because
// it can't; there's no way to distinguish empty sets from not setting
// bits. As a result, users of this endpoint are responsible for
// providing corrected existence views for fields with existence
// tracking. Our batch API does that.
func (api *API) ImportRoaringShard(ctx context.Context, indexName string, shard uint64, req *ImportRoaringShardRequest) error {
index, err := api.Index(ctx, indexName)
if err != nil {
@ -1663,7 +1674,7 @@ func (api *API) ImportRoaringShard(ctx context.Context, indexName string, shard
}
fieldType := field.Options().Type
if err1 = cleanupView(fieldType, &viewUpdate); err1 != nil {
if viewUpdate.View, err1 = field.cleanupViewName(viewUpdate.View); err1 != nil {
return err1
}
@ -1755,27 +1766,6 @@ func (api *API) ImportRoaringShard(ctx context.Context, indexName string, shard
return nil
}
func cleanupView(fieldType string, viewUpdate *RoaringUpdate) error {
// TODO wouldn't hurt to have consolidated logic somewhere for validating view names.
switch fieldType {
case FieldTypeSet, FieldTypeTime:
if viewUpdate.View == "" {
viewUpdate.View = "standard"
}
// add 'standard_' if we just have a time... this is how IDK works by default
if fieldType == FieldTypeTime && !strings.HasPrefix(viewUpdate.View, viewStandard) {
viewUpdate.View = fmt.Sprintf("%s_%s", viewStandard, viewUpdate.View)
}
case FieldTypeInt, FieldTypeDecimal, FieldTypeTimestamp:
if viewUpdate.View == "" {
viewUpdate.View = "bsig_" + viewUpdate.Field
} else if viewUpdate.View != "bsig_"+viewUpdate.Field {
return NewBadRequestError(errors.Errorf("invalid view name (%s) for field %s of type %s", viewUpdate.View, viewUpdate.Field, fieldType))
}
}
return nil
}
// ImportValue is a wrapper around the common code in ImportValueWithTx, which
// currently just translates req.Clear into a clear ImportOption.
func (api *API) ImportValue(ctx context.Context, qcx *Qcx, req *ImportValueRequest, opts ...ImportOption) error {
@ -2029,21 +2019,20 @@ func (api *API) ImportValueWithTx(ctx context.Context, qcx *Qcx, req *ImportValu
return nil
}
func importExistenceColumns(qcx *Qcx, index *Index, columnIDs []uint64, shard uint64) error {
func importExistenceColumns(qcx *Qcx, index *Index, columnIDs []uint64, shard uint64) (err0 error) {
ef := index.existenceField()
if ef == nil {
return nil
}
existenceRowIDs := make([]uint64, len(columnIDs))
// If we don't gratuitously hand-duplicate things in field.Import,
// the fact that fragment.bulkImport rewrites its row and column
// lists can burn us if we don't make a copy before doing the
// existence field write.
columnCopy := make([]uint64, len(columnIDs))
copy(columnCopy, columnIDs)
options := ImportOptions{}
return ef.Import(qcx, existenceRowIDs, columnCopy, nil, shard, &options)
tx, finisher, err := qcx.GetTx(Txo{Write: true, Index: index, Shard: shard})
if err != nil {
return err
}
defer finisher(&err0)
// markExistingInView is simpler/faster than Import, but unusually, we use the
// standard view of the existence field, instead of the existence view of
// a specific field, when doing the index-wide update.
return ef.markExistingInView(tx, columnIDs, viewStandard, shard)
}
// ShardDistribution returns an object representing the distribution of shards
@ -3061,9 +3050,9 @@ func (api *API) Directive(ctx context.Context, d *dax.Directive) error {
}
// DirectiveApplied returns true if the computer's current Directive has been
// applied and is ready to be queried. This it temporary (primarily for tests)
// and needs to be refactored as we improve the logic around mds-to-computer
// communication.
// applied and is ready to be queried. This is temporary (primarily for tests)
// and needs to be refactored as we improve the logic around
// controller-to-computer communication.
func (api *API) DirectiveApplied(ctx context.Context) (bool, error) {
return api.holder.DirectiveApplied(), nil
}
@ -3076,7 +3065,7 @@ func (api *API) SnapshotShardData(ctx context.Context, req *dax.SnapshotShardDat
}
// TODO(jaffee) confirm this node is actually responsible for the given
// shard? Not sure we need to given that this request comes from
// MDS, but might be a belt&suspenders situation.
// the Controller, but might be a belt&suspenders situation.
qtid := req.TableKey.QualifiedTableID()
@ -3307,6 +3296,14 @@ func shardInShards(i dax.ShardNum, s dax.ShardNums) bool {
}
type SchemaAPI interface {
CreateDatabase(context.Context, *dax.Database) error
DropDatabase(context.Context, dax.DatabaseID) error
DatabaseByName(ctx context.Context, dbname dax.DatabaseName) (*dax.Database, error)
DatabaseByID(ctx context.Context, dbid dax.DatabaseID) (*dax.Database, error)
SetDatabaseOption(ctx context.Context, dbid dax.DatabaseID, option string, value string) error
Databases(context.Context, ...dax.DatabaseID) ([]*dax.Database, error)
TableByName(ctx context.Context, tname dax.TableName) (*dax.Table, error)
TableByID(ctx context.Context, tid dax.TableID) (*dax.Table, error)
Tables(ctx context.Context) ([]*dax.Table, error)
@ -3318,8 +3315,49 @@ type SchemaAPI interface {
DeleteField(ctx context.Context, tname dax.TableName, fname dax.FieldName) error
}
// Ensure type implements interface.
var _ SchemaAPI = (*NopSchemaAPI)(nil)
// NopSchemaAPI is a no-op implementation of the SchemaAPI.
type NopSchemaAPI struct{}
func (n *NopSchemaAPI) ClusterName() string {
return ""
}
func (n *NopSchemaAPI) CreateDatabase(context.Context, *dax.Database) error { return nil }
func (n *NopSchemaAPI) DropDatabase(context.Context, dax.DatabaseID) error { return nil }
func (n *NopSchemaAPI) DatabaseByName(ctx context.Context, dbname dax.DatabaseName) (*dax.Database, error) {
return nil, nil
}
func (n *NopSchemaAPI) DatabaseByID(ctx context.Context, dbid dax.DatabaseID) (*dax.Database, error) {
return nil, nil
}
func (n *NopSchemaAPI) SetDatabaseOption(ctx context.Context, dbid dax.DatabaseID, option string, value string) error {
return nil
}
func (n *NopSchemaAPI) Databases(context.Context, ...dax.DatabaseID) ([]*dax.Database, error) {
return nil, nil
}
func (n *NopSchemaAPI) TableByName(ctx context.Context, tname dax.TableName) (*dax.Table, error) {
return nil, nil
}
func (n *NopSchemaAPI) TableByID(ctx context.Context, tid dax.TableID) (*dax.Table, error) {
return nil, nil
}
func (n *NopSchemaAPI) Tables(ctx context.Context) ([]*dax.Table, error) { return nil, nil }
func (n *NopSchemaAPI) CreateTable(ctx context.Context, tbl *dax.Table) error { return nil }
func (n *NopSchemaAPI) CreateField(ctx context.Context, tname dax.TableName, fld *dax.Field) error {
return nil
}
func (n *NopSchemaAPI) DeleteTable(ctx context.Context, tname dax.TableName) error { return nil }
func (n *NopSchemaAPI) DeleteField(ctx context.Context, tname dax.TableName, fname dax.FieldName) error {
return nil
}
type ClusterNode struct {
ID string
Type string
State string
URI string
GRPCURI string

View file

@ -1,4 +1,5 @@
// Copyright 2021 Molecula Corp. All rights reserved.
// Copyright 2022 Molecula Corp. (DBA FeatureBase).
// SPDX-License-Identifier: Apache-2.0
package client
import (
@ -6,8 +7,8 @@ import (
"crypto/tls"
"sync"
"github.com/molecula/featurebase/v3/logger"
pb "github.com/molecula/featurebase/v3/proto"
"github.com/featurebasedb/featurebase/v3/logger"
pb "github.com/featurebasedb/featurebase/v3/proto"
"github.com/pkg/errors"
"google.golang.org/grpc"
"google.golang.org/grpc/connectivity"

View file

@ -7,10 +7,10 @@ import (
"log"
"sync"
"github.com/molecula/featurebase/v3/dax"
"github.com/molecula/featurebase/v3/dax/computer"
"github.com/molecula/featurebase/v3/dax/storage"
"github.com/molecula/featurebase/v3/disco"
"github.com/featurebasedb/featurebase/v3/dax"
"github.com/featurebasedb/featurebase/v3/dax/computer"
"github.com/featurebasedb/featurebase/v3/dax/storage"
"github.com/featurebasedb/featurebase/v3/disco"
"github.com/pkg/errors"
)
@ -35,6 +35,22 @@ func (api *API) ApplyDirective(ctx context.Context, d *dax.Directive) error {
// Handle the operations based on the directive method.
switch d.Method {
case dax.DirectiveMethodDiff:
// In order to prevent adding too much code specific to handling a diff
// directive (e.g. adding something like an `enactDirectiveDiff()`
// method), we are instead going to build a full Directive based on the
// diff, and then proceed normally as if we had received a full
// Directive. We do that by copying the previous Directive and then
// applying the diffs to the copy.
newD := previousDirective.Copy()
// Apply the diffs from the incoming Directive to the new, copied
// Directive.
newD.ApplyDiff(d)
// Now proceed with the new diff as if we had received it as a full diff.
d = newD
case dax.DirectiveMethodFull:
// pass: normal operation
case dax.DirectiveMethodReset:
@ -42,10 +58,6 @@ func (api *API) ApplyDirective(ctx context.Context, d *dax.Directive) error {
if err := api.deleteAllIndexes(ctx); err != nil {
return errors.Wrap(err, "deleting all indexes")
}
if err := api.serverlessStorage.RemoveAll(); err != nil {
return errors.Wrap(err, "removing all managers")
}
// Set previousDirective to empty so the diff handles everything as new.
previousDirective = dax.Directive{}
@ -64,7 +76,7 @@ func (api *API) ApplyDirective(ctx context.Context, d *dax.Directive) error {
// the "enactDirective" stage of ApplyDirective which validates against this
// cached Directive, so it's important that it be set before calling
// enactDirective(). An example: when loading partition data from the
// WriteLogger, there are validations to ensure that the partition being
// Writelogger, there are validations to ensure that the partition being
// loaded is meant to be handled by this node; that validation is done
// against the cached Directive.
// TODO(tlt): despite what this comment says, this logic is not sound; we
@ -250,7 +262,7 @@ func (api *API) enactTables(ctx context.Context, fromD, toD *dax.Directive) erro
// Remove all indexes that are no longer part of the directive.
for _, tkey := range sc.removed() {
idx := string(tkey)
if err := api.holder.deleteIndex(idx); err != nil {
if err := api.DeleteIndex(ctx, idx); err != nil {
return errors.Wrapf(err, "deleting index: %s", tkey)
}
}
@ -328,7 +340,18 @@ func (api *API) pushJobsTableKeys(ctx context.Context, jobs chan<- directiveJobT
// Get the diff between from/to directive.partitions.
partComp := newPartitionsComparer(fromD.TranslatePartitionsMap(), toPartitionsMap)
// Loop over the partition map and load from WriteLogger.
// Remove any partitions which are no longer assigned to this worker.
// TODO(tlt): currently, this is just removing the file lock on the
// resource; it's not actually removing the resource from the local
// computer. We should do that.
for tkey, partitions := range partComp.removed() {
qtid := tkey.QualifiedTableID()
for _, partition := range partitions {
api.serverlessStorage.RemoveTableKeyResource(qtid, partition)
}
}
// Loop over the partition map and load from Writelogger.
for tkey, partitions := range partComp.added() {
// Get index in order to find the translate stores (by partition) for
// the table.
@ -415,7 +438,18 @@ func (api *API) pushJobsFieldKeys(ctx context.Context, jobs chan<- directiveJobT
// Get the diff between from/to directive.fields.
fieldComp := newFieldsComparer(fromD.TranslateFieldsMap(), toD.TranslateFieldsMap())
// Loop over the field map and load from WriteLogger.
// Remove any field keys which are no longer assigned to this worker.
// TODO(tlt): currently, this is just removing the file lock on the
// resource; it's not actually removing the resource from the local
// computer. We should do that.
for tkey, fields := range fieldComp.removed() {
qtid := tkey.QualifiedTableID()
for _, field := range fields {
api.serverlessStorage.RemoveFieldKeyResource(qtid, field)
}
}
// Loop over the field map and load from Writelogger.
for tkey, fields := range fieldComp.added() {
for _, field := range fields {
jobs <- directiveJobFieldKeys{
@ -499,7 +533,19 @@ func (api *API) pushJobsShards(ctx context.Context, jobs chan<- directiveJobType
// Get the diff between from/to directive shards.
shardComp := newShardsComparer(fromD.ComputeShardsMap(), shardMap)
// Loop over the shard map and load from WriteLogger.
// Remove any shards which are no longer assigned to this worker.
// TODO(tlt): currently, this is just removing the file lock on the
// resource; it's not actually removing the resource from the local
// computer. We should do that.
for tkey, shards := range shardComp.removed() {
qtid := tkey.QualifiedTableID()
for _, shard := range shards {
partition := dax.PartitionNum(disco.ShardToShardPartition(string(tkey), uint64(shard), disco.DefaultPartitionN))
api.serverlessStorage.RemoveShardResource(qtid, partition, shard)
}
}
// Loop over the shard map and load from Writelogger.
for tkey, shards := range shardComp.added() {
for _, shard := range shards {
jobs <- directiveJobShards{
@ -934,7 +980,7 @@ func createField(idx *Index, fld *dax.Field) error {
return errors.Wrapf(err, "creating field options from field: %s", fld.Name)
}
if _, err := idx.CreateField(string(fld.Name), "", opts...); err != nil {
if _, err := idx.createNullableField(string(fld.Name), "", opts...); err != nil {
return errors.Wrapf(err, "creating field on index: %s", fld.Name)
}
return nil

View file

@ -4,10 +4,10 @@ import (
"context"
"testing"
pilosa "github.com/molecula/featurebase/v3"
"github.com/molecula/featurebase/v3/dax"
daxtest "github.com/molecula/featurebase/v3/dax/test"
"github.com/molecula/featurebase/v3/test"
pilosa "github.com/featurebasedb/featurebase/v3"
"github.com/featurebasedb/featurebase/v3/dax"
daxtest "github.com/featurebasedb/featurebase/v3/dax/test"
"github.com/featurebasedb/featurebase/v3/test"
"github.com/stretchr/testify/assert"
)
@ -30,7 +30,7 @@ func TestAPI_Directive(t *testing.T) {
// Empty directive (and empty holder).
{
d := &dax.Directive{
Method: dax.DirectiveMethodDiff,
Method: dax.DirectiveMethodFull,
Version: 1,
}
err := api.ApplyDirective(ctx, d)
@ -41,7 +41,7 @@ func TestAPI_Directive(t *testing.T) {
// Add a new table.
{
d := &dax.Directive{
Method: dax.DirectiveMethodDiff,
Method: dax.DirectiveMethodFull,
Tables: []*dax.QualifiedTable{
tbl1,
},
@ -55,7 +55,7 @@ func TestAPI_Directive(t *testing.T) {
// Add a new table, and keep the existing table.
{
d := &dax.Directive{
Method: dax.DirectiveMethodDiff,
Method: dax.DirectiveMethodFull,
Tables: []*dax.QualifiedTable{
tbl1,
tbl2,
@ -70,7 +70,7 @@ func TestAPI_Directive(t *testing.T) {
// Add a new table and remove one of the existing tables.
{
d := &dax.Directive{
Method: dax.DirectiveMethodDiff,
Method: dax.DirectiveMethodFull,
Tables: []*dax.QualifiedTable{
tbl2,
tbl3,

View file

@ -1,4 +1,5 @@
// Copyright 2021 Molecula Corp. All rights reserved.
// Copyright 2022 Molecula Corp. (DBA FeatureBase).
// SPDX-License-Identifier: Apache-2.0
package pilosa_test
import (
@ -21,14 +22,14 @@ import (
"testing"
"time"
pilosa "github.com/featurebasedb/featurebase/v3"
"github.com/featurebasedb/featurebase/v3/authn"
"github.com/featurebasedb/featurebase/v3/roaring"
"github.com/featurebasedb/featurebase/v3/server"
"github.com/featurebasedb/featurebase/v3/shardwidth"
"github.com/featurebasedb/featurebase/v3/test"
. "github.com/featurebasedb/featurebase/v3/vprint" // nolint:staticcheck
"github.com/golang-jwt/jwt"
pilosa "github.com/molecula/featurebase/v3"
"github.com/molecula/featurebase/v3/authn"
"github.com/molecula/featurebase/v3/roaring"
"github.com/molecula/featurebase/v3/server"
"github.com/molecula/featurebase/v3/shardwidth"
"github.com/molecula/featurebase/v3/test"
. "github.com/molecula/featurebase/v3/vprint" // nolint:staticcheck
"golang.org/x/sync/errgroup"
)
@ -836,7 +837,7 @@ func TestAPI_IDAlloc(t *testing.T) {
t.Fatalf("obtaining random bytes: %v", err)
}
ids3, err := primary.ReserveIDs(key, session, 0, 2)
var esync pilosa.ErrIDOffsetDesync
var esync pilosa.IDOffsetDesyncError
if errors.As(err, &esync) {
if esync.Requested != 0 {
t.Errorf("incorrect requested offset in error: provided %d but got %d", 0, esync.Requested)

View file

@ -14,10 +14,10 @@ import (
"github.com/apache/arrow/go/v10/arrow"
"github.com/apache/arrow/go/v10/arrow/array"
"github.com/apache/arrow/go/v10/arrow/memory"
"github.com/featurebasedb/featurebase/v3/pql"
"github.com/featurebasedb/featurebase/v3/tracing"
"github.com/featurebasedb/featurebase/v3/vprint"
"github.com/gomem/gomem/pkg/dataframe"
"github.com/molecula/featurebase/v3/pql"
"github.com/molecula/featurebase/v3/tracing"
"github.com/molecula/featurebase/v3/vprint"
"github.com/pkg/errors"
ivy "robpike.io/ivy/arrow"
@ -95,7 +95,7 @@ func IvyReduce(reduceCode string, opCode string, opt *ExecOptions) (func(ctx con
col := value.ToArrowColumn(accumulator, pool)
return dataframe.NewDataFrameFromColumns(pool, []arrow.Column{*col})
}
// only acutally reduce on the initiating node i hate the network
// only actually reduce on the initiating node i hate the network
// over head but oh well
ctxIvy.AssignGlobal("_", accumulator)
ok, err := runIvyString(ctxIvy, reduceCode)
@ -541,7 +541,7 @@ func (sf *ShardFile) Save(name string) error {
if sf.table != nil {
// we append if there was existing file
column := sf.table.Column(col)
// if primative type
// if primitive type
switch column.DataType() {
case arrow.BinaryTypes.String:
chunks = sf.buildFromStrings(col, mem)

View file

@ -17,9 +17,9 @@ import (
"github.com/apache/arrow/go/v10/parquet"
"github.com/apache/arrow/go/v10/parquet/file"
"github.com/apache/arrow/go/v10/parquet/pqarrow"
"github.com/featurebasedb/featurebase/v3/pql"
"github.com/featurebasedb/featurebase/v3/tracing"
"github.com/gomem/gomem/pkg/dataframe"
"github.com/molecula/featurebase/v3/pql"
"github.com/molecula/featurebase/v3/tracing"
"github.com/pkg/errors"
)
@ -60,7 +60,7 @@ func (e *executor) executeArrow(ctx context.Context, qcx *Qcx, index string, c *
mu.Unlock()
return e.executeArrowShard(ctx, qcx, index, c, shard, pool, columnFilter)
}
tables := make([]*basicTable, 0)
tables := make([]*BasicTable, 0)
reduceFn := func(ctx context.Context, prev, v interface{}) interface{} {
mu.Lock()
@ -70,7 +70,7 @@ func (e *executor) executeArrow(ctx context.Context, qcx *Qcx, index string, c *
return prev
}
switch t := v.(type) {
case *basicTable:
case *BasicTable:
if t.resolver != nil {
mu.Lock()
@ -93,104 +93,108 @@ func (e *executor) executeArrow(ctx context.Context, qcx *Qcx, index string, c *
return nil, err
}
if len(tables) == 0 {
return &basicTable{name: "empty"}, nil
return &BasicTable{name: "empty"}, nil
}
tbl := Concat(tables[0].Schema(), tables, pool)
r := dataframe.NewChunkResolver(tbl.Column(0))
return &basicTable{resolver: &r, table: tbl}, nil
return &BasicTable{resolver: &r, table: tbl}, nil
}
type basicTable struct {
type BasicTable struct {
resolver dataframe.Resolver
table arrow.Table
filtered bool
name string
}
func (st *basicTable) Name() string {
func (st *BasicTable) Name() string {
return st.name
}
func (st *basicTable) Schema() *arrow.Schema {
func (st *BasicTable) Schema() *arrow.Schema {
if st.table != nil {
return st.table.Schema()
}
return &arrow.Schema{}
}
func (st *basicTable) IsFiltered() bool {
func (st *BasicTable) IsFiltered() bool {
return st.filtered
}
func (st *basicTable) NumRows() int64 {
func (st *BasicTable) NumRows() int64 {
if st.resolver == nil {
return 0
}
return int64(st.resolver.NumRows())
}
func (st *basicTable) NumCols() int64 {
func (st *BasicTable) NumCols() int64 {
if st.table != nil {
return st.table.NumCols()
}
return 0
}
func (st *basicTable) Column(i int) *arrow.Column {
func (st *BasicTable) Column(i int) *arrow.Column {
if st.table != nil {
return st.table.Column(i)
}
return nil
}
func (st *basicTable) Retain() {
func (st *BasicTable) Retain() {
if st.table != nil {
st.table.Retain()
}
}
func (st *basicTable) Release() {
func (st *BasicTable) Release() {
if st.table != nil {
st.table.Retain()
}
}
func (st *basicTable) Get(column, row int) interface{} {
func (st *BasicTable) Get(column, row int) interface{} {
field := st.Schema().Field(column)
c, i := st.resolver.Resolve(row)
nullable := field.Nullable
chunk := st.Column(column).Data().Chunk(c)
// TODO(twg) 2023/01/26 potential NULL support?
if nullable && chunk.IsNull(i) {
return nil
}
switch field.Type.(type) {
// case *arrow.BooleanType:
// v := chunk.(*array.Boolean).BooleanValues()
// return v[i]
case *arrow.BooleanType:
return chunk.(*array.Boolean).Value(i)
case *arrow.Int8Type:
v := chunk.(*array.Int8).Int8Values()
return v[i]
return int64(v[i])
case *arrow.Int16Type:
v := chunk.(*array.Int16).Int16Values()
return v[i]
return int64(v[i])
case *arrow.Int32Type:
v := chunk.(*array.Int32).Int32Values()
return v[i]
return int64(v[i])
case *arrow.Int64Type:
v := chunk.(*array.Int64).Int64Values()
return v[i]
return int64(v[i])
case *arrow.Uint8Type:
v := chunk.(*array.Uint8).Uint8Values()
return v[i]
return uint64(v[i])
case *arrow.Uint16Type:
v := chunk.(*array.Uint16).Uint16Values()
return v[i]
return uint64(v[i])
case *arrow.Uint32Type:
v := chunk.(*array.Uint32).Uint32Values()
return v[i]
return uint64(v[i])
case *arrow.Uint64Type:
v := chunk.(*array.Uint64).Uint64Values()
return v[i]
case *arrow.Float32Type:
v := chunk.(*array.Float32).Float32Values()
return v[i]
return float64(v[i])
case *arrow.Float64Type:
v := chunk.(*array.Float64).Float64Values()
return v[i]
@ -265,7 +269,7 @@ func appendData(bldr array.Builder, v interface{}) {
}
}
func Concat(schema *arrow.Schema, tables []*basicTable, mem memory.Allocator) arrow.Table {
func Concat(schema *arrow.Schema, tables []*BasicTable, mem memory.Allocator) arrow.Table {
if len(tables) == 1 {
if !tables[0].IsFiltered() {
return tables[0]
@ -307,7 +311,7 @@ func Concat(schema *arrow.Schema, tables []*basicTable, mem memory.Allocator) ar
return array.NewTable(schema, cols, -1)
}
func (st *basicTable) MarshalJSON() ([]byte, error) {
func (st *BasicTable) MarshalJSON() ([]byte, error) {
results := make(map[string]interface{})
n := 0
if st.table != nil {
@ -326,10 +330,10 @@ func (st *basicTable) MarshalJSON() ([]byte, error) {
return json.Marshal(results)
}
func BasicTableFromArrow(table arrow.Table, mem memory.Allocator) *basicTable {
func BasicTableFromArrow(table arrow.Table, mem memory.Allocator) *BasicTable {
col := table.Column(0)
r := dataframe.NewChunkResolver(col)
return &basicTable{resolver: &r, table: table}
return &BasicTable{resolver: &r, table: table}
}
func filterColumns(filters []string, table arrow.Table) arrow.Table {
@ -359,7 +363,7 @@ func filterColumns(filters []string, table arrow.Table) arrow.Table {
return array.NewTable(filterdSchema, cols, table.NumRows())
}
func (e *executor) executeArrowShard(ctx context.Context, qcx *Qcx, index string, c *pql.Call, shard uint64, pool memory.Allocator, columnFilter []string) (*basicTable, error) {
func (e *executor) executeArrowShard(ctx context.Context, qcx *Qcx, index string, c *pql.Call, shard uint64, pool memory.Allocator, columnFilter []string) (*BasicTable, error) {
name := fmt.Sprintf("a. %v", shard)
span, _ := tracing.StartSpanFromContext(ctx, "Executor.executeArrowShard")
defer span.Finish()
@ -373,7 +377,7 @@ func (e *executor) executeArrowShard(ctx context.Context, qcx *Qcx, index string
filter = row
if !filter.Any() {
// no need to actuall run the query for its not operating against any values
return &basicTable{name: name}, nil
return &BasicTable{name: name}, nil
}
}
//
@ -387,7 +391,7 @@ func (e *executor) executeArrowShard(ctx context.Context, qcx *Qcx, index string
fname := idx.GetDataFramePath(shard)
if !e.dataFrameExists(fname) {
return &basicTable{name: name}, nil
return &BasicTable{name: name}, nil
}
table, err := e.getDataTable(ctx, fname, pool)
@ -407,7 +411,7 @@ func (e *executor) executeArrowShard(ctx context.Context, qcx *Qcx, index string
resolver = &p
if filter != nil {
if len(ids) == 0 {
return &basicTable{name: name}, nil
return &BasicTable{name: name}, nil
}
resolver, err = filterDataframe(resolver, pool, ids)
if err != nil {
@ -415,7 +419,7 @@ func (e *executor) executeArrowShard(ctx context.Context, qcx *Qcx, index string
}
}
table.Retain()
return &basicTable{resolver: resolver, table: table, filtered: filter != nil, name: name}, nil
return &BasicTable{resolver: resolver, table: table, filtered: filter != nil, name: name}, nil
}
func (e *executor) dataFrameExists(fname string) bool {
@ -474,7 +478,7 @@ func readTableArrow(filename string, mem memory.Allocator) (arrow.Table, error)
return nil, err
}
defer rr.Close()
records := make([]arrow.Record, rr.NumRecords(), rr.NumRecords())
records := make([]arrow.Record, rr.NumRecords())
i := 0
for {
rec, err := rr.Read()

View file

@ -1,8 +1,9 @@
// Copyright 2021 Molecula Corp. All rights reserved.
// Copyright 2022 Molecula Corp. (DBA FeatureBase).
// SPDX-License-Identifier: Apache-2.0
package pilosa
import (
"github.com/molecula/featurebase/v3/testhook"
"github.com/featurebasedb/featurebase/v3/testhook"
)
var NewAuditor func() testhook.Auditor = NewNopAuditor

View file

@ -1,11 +1,12 @@
// Copyright 2021 Molecula Corp. All rights reserved.
// Copyright 2022 Molecula Corp. (DBA FeatureBase).
// SPDX-License-Identifier: Apache-2.0
package pilosa
import (
"fmt"
"reflect"
"github.com/molecula/featurebase/v3/testhook"
"github.com/featurebasedb/featurebase/v3/testhook"
)
// These audit hooks are desireable during testing, but not in

View file

@ -1,4 +1,5 @@
// Copyright 2021 Molecula Corp. All rights reserved.
// Copyright 2022 Molecula Corp. (DBA FeatureBase).
// SPDX-License-Identifier: Apache-2.0
package pilosa_test
import (
@ -6,8 +7,8 @@ import (
"os"
"reflect"
"github.com/molecula/featurebase/v3"
"github.com/molecula/featurebase/v3/testhook"
"github.com/featurebasedb/featurebase/v3"
"github.com/featurebasedb/featurebase/v3/testhook"
)
// AuditLeaksOn is a global switch to turn on resource

View file

@ -1,4 +1,5 @@
// Copyright 2021 Molecula Corp. All rights reserved.
// Copyright 2022 Molecula Corp. (DBA FeatureBase).
// SPDX-License-Identifier: Apache-2.0
// Package authn handles authentication
package authn
@ -19,11 +20,14 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
"github.com/molecula/featurebase/v3/logger"
"github.com/featurebasedb/featurebase/v3/logger"
"github.com/pkg/errors"
"golang.org/x/oauth2"
)
// AuthContextKey is a unique type to prevent collisions when using context.WithValue()
type AuthContextKey string
const (
// AccessCookieName is the name of the cookie that holds the access token.
AccessCookieName = "molecula-chip"
@ -33,6 +37,12 @@ const (
// RefreshHeaderName is the name of the header that holds the refresh token.
RefreshHeaderName = "X-Molecula-Refresh-Token"
// ContextValueAccessToken is the key used to set AccessTokens in a ctx.
ContextValueAccessToken = AuthContextKey("Access")
// ContextValueRefreshToken is the key used to set RefreshTokens in a ctx.
ContextValueRefreshToken = AuthContextKey("Refresh")
)
// cachedGroups is used to hold groups and when they were last cached
@ -163,7 +173,7 @@ func (a *Auth) refreshToken(access, refresh string) (string, string, error) {
// it is caller's responsibility to inform the user that the access token has been refreshed
func (a *Auth) Authenticate(access, refresh string) (*UserInfo, error) {
// clean up the cache every 30 minutes or so
if time.Now().Sub(a.lastCacheClean) >= 30*time.Minute {
if time.Since(a.lastCacheClean) >= 30*time.Minute {
a.cleanCache()
}
@ -229,7 +239,7 @@ func (a *Auth) Authenticate(access, refresh string) (*UserInfo, error) {
func (a *Auth) cleanCache() {
for access, tkn := range a.groupsCache {
// if it's been more than 24 hours since the groups were cached
if time.Now().Sub(tkn.cacheTime) >= 24*time.Hour {
if time.Since(tkn.cacheTime) >= 24*time.Hour {
// remove it from our cache
delete(a.groupsCache, access)
}
@ -292,7 +302,7 @@ func (a *Auth) getGroups(token string) ([]Group, error) {
var groups Groups
gc, ok := a.groupsCache[token]
if ok && (time.Now().Sub(gc.cacheTime) < a.cacheTTL) && len(gc.groups) > 0 {
if ok && (time.Since(gc.cacheTime) < a.cacheTTL) && len(gc.groups) > 0 {
return gc.groups, nil
}

View file

@ -16,8 +16,8 @@ import (
"testing"
"time"
"github.com/featurebasedb/featurebase/v3/logger"
"github.com/golang-jwt/jwt"
"github.com/molecula/featurebase/v3/logger"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
)
@ -67,7 +67,7 @@ func TestSetGRPCMetadata(t *testing.T) {
"otherCookies": {"cookie": []string{a.accessCookieName + "=something", "blah=blah"}},
} {
t.Run(name, func(t *testing.T) {
ogCookies, _ := md["cookie"]
ogCookies := md["cookie"]
ctx := grpc.NewContextWithServerTransportStream(
metadata.NewIncomingContext(context.TODO(),
md,
@ -280,8 +280,7 @@ func TestAuthenticate(t *testing.T) {
a.groupsCache[token] = cachedGroups{time.Now(), test.groups}
}
if test.refresh {
var srv *httptest.Server
srv = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
t.Fatalf("unexpected error: %v", err)
}

View file

@ -1,16 +1,5 @@
// Copyright 2017 Pilosa Corp.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Copyright 2022 Molecula Corp. (DBA FeatureBase).
// SPDX-License-Identifier: Apache-2.0
package authz
@ -18,7 +7,7 @@ import (
"fmt"
"io"
"github.com/molecula/featurebase/v3/authn"
"github.com/featurebasedb/featurebase/v3/authn"
"gopkg.in/yaml.v2"
)

View file

@ -1,16 +1,5 @@
// Copyright 2017 Pilosa Corp.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Copyright 2022 Molecula Corp. (DBA FeatureBase).
// SPDX-License-Identifier: Apache-2.0
package authz_test
import (
@ -20,8 +9,8 @@ import (
"strings"
"testing"
"github.com/molecula/featurebase/v3/authn"
"github.com/molecula/featurebase/v3/authz"
"github.com/featurebasedb/featurebase/v3/authn"
"github.com/featurebasedb/featurebase/v3/authz"
)
func TestAuth_ReadPermissionsFile(t *testing.T) {

View file

@ -2,10 +2,10 @@ ARG GO_VERSION=1.19
FROM golang:${GO_VERSION}
WORKDIR /go/src/github.com/molecula/featurebase/
WORKDIR /go/src/github.com/featurebasedb/featurebase/
COPY . .
WORKDIR /go/src/github.com/molecula/featurebase/batch/
WORKDIR /go/src/github.com/featurebasedb/featurebase/batch/
CMD ["go","test","-v","-mod=vendor","-tags=odbc,dynamic","./..."]

View file

@ -10,12 +10,12 @@ import (
"sync"
"time"
featurebase "github.com/molecula/featurebase/v3"
"github.com/molecula/featurebase/v3/batch/egpool"
"github.com/molecula/featurebase/v3/dax"
"github.com/molecula/featurebase/v3/logger"
"github.com/molecula/featurebase/v3/pql"
"github.com/molecula/featurebase/v3/roaring"
featurebase "github.com/featurebasedb/featurebase/v3"
"github.com/featurebasedb/featurebase/v3/batch/egpool"
"github.com/featurebasedb/featurebase/v3/dax"
"github.com/featurebasedb/featurebase/v3/logger"
"github.com/featurebasedb/featurebase/v3/pql"
"github.com/featurebasedb/featurebase/v3/roaring"
"github.com/pkg/errors"
)
@ -23,6 +23,7 @@ import (
const (
DefaultKeyTranslateBatchSize = 100000
existenceFieldName = "_exists"
existenceViewName = "existence" // this should match top level featurebase viewExistence
)
// TODO if using column translation, column ids might get way out of
@ -573,7 +574,11 @@ func (b *Batch) Add(rec Row) error {
case int64:
b.values[field.Name] = append(b.values[field.Name], val)
case []string:
if len(val) == 0 {
// note that a length of 0 can be valid, and represents an
// empty set. an empty set counts as a non-NULL value for
// SQL purposes -- it means the existence view bit should
// get set.
if val == nil {
continue
}
rowIDSets, ok := b.rowIDSets[field.Name]
@ -608,7 +613,11 @@ func (b *Batch) Add(rec Row) error {
}
b.rowIDSets[field.Name] = append(rowIDSets, rowIDs)
case []uint64:
if len(val) == 0 {
// note that a length of 0 can be valid, and represents an
// empty set. an empty set counts as a non-NULL value for
// SQL purposes -- it means the existence view bit should
// get set.
if val == nil {
continue
}
rowIDSets, ok := b.rowIDSets[field.Name]
@ -663,6 +672,9 @@ func (b *Batch) Add(rec Row) error {
for i, uval := range rec.Clears {
field := b.header[i]
if field.Options.Type == featurebase.FieldTypeMutex && uval != nil {
return errors.Errorf("individual-bit clears not allowed on mutex fields; use nil to clear a mutex")
}
if _, ok := b.clearRowIDs[i]; !ok {
b.clearRowIDs[i] = make(map[int]uint64)
}
@ -718,7 +730,7 @@ func (b *Batch) Add(rec Row) error {
return nil
}
// ErrBatchNowFull — similar to io.EOF — is a marker error to notify the user of
// ErrBatchNowFull — similar to io.EOF — is a marker error to notify the user of
// a batch that it is time to call Import.
var ErrBatchNowFull = errors.New("batch is now full - you cannot add any more records (though the one you just added was accepted)")
@ -1245,7 +1257,7 @@ func (b *Batch) doImport(frags, clearFrags fragments) error {
}
ferr := b.importer.ImportRoaringBitmap(ctx, b.tbl.ID, fld, shard, viewMap, false)
b.log.Debugf("imp-roar field: %s, shard:%d, views:%d %v", field, shard, len(clearViewMap), time.Since(starty))
b.log.Debugf("imp-roar field: %s, shard:%d, views:%d %v", field, shard, len(viewMap), time.Since(starty))
return errors.Wrapf(ferr, "importing data for %s", field)
})
}
@ -1343,6 +1355,7 @@ func (b *Batch) makeFragments(frags, clearFrags fragments) (fragments, fragments
curShard := ^uint64(0) // impossible sentinel value for shard.
var curBM *roaring.Bitmap
var clearBM *roaring.Bitmap
var existCurBM *roaring.Bitmap
for j := range b.ids {
col := b.ids[j]
row := nilSentinel
@ -1355,8 +1368,12 @@ func (b *Batch) makeFragments(frags, clearFrags fragments) (fragments, fragments
if col/shardWidth != curShard {
curShard = col / shardWidth
// the API treats "" as standard
curBM = frags.GetOrCreate(curShard, field.Name, "")
clearBM = clearFrags.GetOrCreate(curShard, field.Name, "")
if opts.ActuallyTrackingExistence() {
existCurBM = frags.GetOrCreate(curShard, field.Name, existenceViewName)
}
}
if row != nilSentinel {
// TODO this is super ugly, but we want to avoid setting
@ -1366,6 +1383,9 @@ func (b *Batch) makeFragments(frags, clearFrags fragments) (fragments, fragments
// the NoStandardView case would be great.
if !(opts.Type == featurebase.FieldTypeTime && opts.NoStandardView) {
curBM.DirectAdd(row*shardWidth + (col % shardWidth))
if opts.ActuallyTrackingExistence() {
existCurBM.DirectAdd(col % shardWidth)
}
}
if opts.Type == featurebase.FieldTypeTime {
views, err := b.times[j].views(opts.TimeQuantum)
@ -1386,6 +1406,16 @@ func (b *Batch) makeFragments(frags, clearFrags fragments) (fragments, fragments
// we want to make sure that at this point, the "set"
// fragments don't contain the bit that we're clearing
curBM.DirectRemoveN(clearRow*shardWidth + (col % shardWidth))
// Because this is RowIDs, not RowIDSets, there's only one
// bit. We should not be setting the existence bit based on
// this value, if we're actually clearing it. This doesn't
// mean we will clear an existing existence bit, though.
// The case where we would clear an existence bit is the
// case where someone specified row[mutexField].Clears = nil,
// which is far from here.
if opts.ActuallyTrackingExistence() {
existCurBM.DirectRemoveN(col % shardWidth)
}
}
}
}
@ -1404,14 +1434,23 @@ func (b *Batch) makeFragments(frags, clearFrags fragments) (fragments, fragments
opts := field.Options
curShard := ^uint64(0) // impossible sentinel value for shard.
var curBM *roaring.Bitmap
var existCurBM *roaring.Bitmap
for j := range b.ids {
col, rowIDs := b.ids[j], rowIDSets[j]
if len(rowIDs) == 0 {
continue
}
if col/shardWidth != curShard {
curShard = col / shardWidth
curBM = frags.GetOrCreate(curShard, fname, "")
if opts.ActuallyTrackingExistence() {
existCurBM = frags.GetOrCreate(curShard, fname, existenceViewName)
}
}
if len(rowIDs) == 0 {
// you can validly specify an empty set, which is not the same as a null,
// but which still ought to set the existence bit if we're tracking that.
if opts.ActuallyTrackingExistence() && rowIDs != nil {
existCurBM.DirectAdd(col % shardWidth)
}
continue
}
// TODO this is super ugly, but we want to avoid setting
// bits on the standard view in the specific case when
@ -1422,6 +1461,9 @@ func (b *Batch) makeFragments(frags, clearFrags fragments) (fragments, fragments
for _, row := range rowIDs {
curBM.DirectAdd(row*shardWidth + (col % shardWidth))
}
if opts.ActuallyTrackingExistence() {
existCurBM.DirectAdd(col % shardWidth)
}
}
if opts.Type == featurebase.FieldTypeTime {
views, err := b.times[j].views(opts.TimeQuantum)
@ -1549,6 +1591,11 @@ func (b *Batch) makeSingleValFragments(frags, clearFrags fragments) (fragments,
shard := ids[0] / shardWidth
bitmap := frags.GetOrCreate(shard, field.Name, "standard")
clearBM := clearFrags.GetOrCreate(shard, field.Name, "standard")
var existBM, existClearBM *roaring.Bitmap
if field.Options.ActuallyTrackingExistence() {
existBM = frags.GetOrCreate(shard, field.Name, existenceViewName)
existClearBM = clearFrags.GetOrCreate(shard, field.Name, existenceViewName)
}
for i, id := range ids {
if i+1 < len(ids) {
// we only want the last value set for each id
@ -1561,6 +1608,10 @@ func (b *Batch) makeSingleValFragments(frags, clearFrags fragments) (fragments,
shard = id / shardWidth
bitmap = frags.GetOrCreate(shard, field.Name, "standard")
clearBM = clearFrags.GetOrCreate(shard, field.Name, "standard")
if field.Options.ActuallyTrackingExistence() {
existBM = frags.GetOrCreate(shard, field.Name, existenceViewName)
existClearBM = clearFrags.GetOrCreate(shard, field.Name, existenceViewName)
}
}
fragmentColumn := id % shardWidth
clearBM.Add(fragmentColumn) // Will use this to clear columns.
@ -1568,6 +1619,11 @@ func (b *Batch) makeSingleValFragments(frags, clearFrags fragments) (fragments,
// clearSentinel is used for deletion
// so this value should only be added if its not clearSentinel
bitmap.Add(row*shardWidth + fragmentColumn)
if field.Options.ActuallyTrackingExistence() {
existBM.Add(fragmentColumn)
}
} else if field.Options.ActuallyTrackingExistence() {
existClearBM.Add(fragmentColumn)
}
}
}
@ -1596,6 +1652,11 @@ func (b *Batch) makeSingleValFragments(frags, clearFrags fragments) (fragments,
fragmentColumn := recID % shardWidth
clearBM.Add(fragmentColumn)
if field.Options.ActuallyTrackingExistence() {
existClearBM := clearFrags.GetOrCreate(shard, field.Name, existenceViewName)
existClearBM.Add(fragmentColumn)
}
}
}
@ -1618,6 +1679,10 @@ func (b *Batch) makeSingleValFragments(frags, clearFrags fragments) (fragments,
fragmentColumn := recID % shardWidth
clearBM.Add(fragmentColumn)
if field.Options.ActuallyTrackingExistence() {
exist := frags.GetOrCreate(shard, field.Name, existenceViewName)
exist.Add(fragmentColumn)
}
if boolVal {
bitmap.Add(trueRowOffset + fragmentColumn)

View file

@ -12,9 +12,9 @@ import (
"testing"
"time"
featurebase "github.com/molecula/featurebase/v3"
"github.com/molecula/featurebase/v3/client"
"github.com/molecula/featurebase/v3/pql"
featurebase "github.com/featurebasedb/featurebase/v3"
"github.com/featurebasedb/featurebase/v3/client"
"github.com/featurebasedb/featurebase/v3/pql"
"github.com/stretchr/testify/assert"
"github.com/pkg/errors"
@ -103,6 +103,12 @@ func testStringSliceCombos(t *testing.T, importer featurebase.Importer, sapi fea
Index: idx.Name,
Query: "TopN(a1, n=10)",
})
if resp.Err != nil {
t.Fatalf("unexpected error from TopN query: %v", resp.Err)
}
if len(resp.Results) < 1 {
t.Fatalf("expected non-empty result set, got empty results")
}
pairsField, ok := resp.Results[0].(*featurebase.PairsField)
assert.True(t, ok, "wrong return type: %T", resp.Results[0])
@ -508,10 +514,11 @@ func testStringSliceEmptyAndNil(t *testing.T, importer featurebase.Importer, sap
{
Name: "strslice",
Options: featurebase.FieldOptions{
Type: featurebase.FieldTypeSet,
Keys: true,
CacheType: featurebase.CacheTypeRanked,
CacheSize: 100,
Type: featurebase.FieldTypeSet,
Keys: true,
CacheType: featurebase.CacheTypeRanked,
CacheSize: 100,
TrackExistence: true,
},
},
},
@ -611,6 +618,14 @@ func testStringSliceEmptyAndNil(t *testing.T, importer featurebase.Importer, sap
pql: "Row(strslice='z')",
exp: []uint64{2},
},
{
pql: "Row(strslice==null)",
exp: []uint64{1},
},
{
pql: "Row(strslice!=null)",
exp: []uint64{0, 2, 3, 4},
},
}
for i, test := range tests {
t.Run(fmt.Sprintf("test-%d", i), func(t *testing.T) {
@ -2045,7 +2060,7 @@ func mutexClearRegression(t *testing.T, importer featurebase.Importer, sapi feat
}
col := uint64(0)
row := uint64(1)
row := uint64(0)
for i := uint64(0); i <= 21; i++ {
col = (i%2+1)*featurebase.ShardWidth + i%5
row = i % 3
@ -2126,7 +2141,7 @@ func mutexNilClearID(t *testing.T, importer featurebase.Importer, sapi featureba
}
col := uint64(0)
row := uint64(1)
row := uint64(0)
// populate mutex with some data
for i := uint64(0); i < 11; i++ {
col = (i%2+1)*featurebase.ShardWidth + i%5
@ -2341,3 +2356,58 @@ func testImportBatchBools(t *testing.T, importer featurebase.Importer, sapi feat
assert.True(t, ok, "wrong return type: %T", resp.Results[0])
assert.Equal(t, uint64(2), count)
}
func TestConvert(t *testing.T) {
t.Run("timestampToInt", func(t *testing.T) {
tests := []struct {
unit TimeUnit
ts string
exp int64
}{
{unit: "s", ts: "2022-01-01T00:00:00Z", exp: 1640995200},
{unit: "ms", ts: "2022-01-01T00:00:00Z", exp: 1640995200000},
{unit: "us", ts: "2022-01-01T00:00:00Z", exp: 1640995200000000},
{unit: "ns", ts: "2022-01-01T00:00:00Z", exp: 1640995200000000000},
{unit: "x", ts: "2022-01-01T00:00:00Z", exp: 0},
}
for i, test := range tests {
t.Run(fmt.Sprintf("test-%d", i), func(t *testing.T) {
ts, err := time.Parse(time.RFC3339, test.ts)
assert.NoError(t, err)
v := timestampToInt(test.unit, ts)
assert.Equal(t, test.exp, v)
})
}
})
t.Run("Int64ToTimestamp", func(t *testing.T) {
tests := []struct {
unit TimeUnit
epoch string
val int64
exp time.Time
}{
{
unit: "ms",
epoch: "2022-01-01T00:00:00Z",
val: 0,
exp: time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC),
},
{
unit: "s",
epoch: "2022-01-01T00:00:00Z",
val: 86400,
exp: time.Date(2022, 1, 2, 0, 0, 0, 0, time.UTC),
},
}
for i, test := range tests {
t.Run(fmt.Sprintf("test-%d", i), func(t *testing.T) {
epoch, err := time.Parse(time.RFC3339, test.epoch)
assert.NoError(t, err)
ts, err := Int64ToTimestamp(test.unit, epoch, test.val)
assert.NoError(t, err)
assert.Equal(t, test.exp, ts)
})
}
})
}

20
batch/batcher.go Normal file
View file

@ -0,0 +1,20 @@
package batch
import (
"time"
"github.com/featurebasedb/featurebase/v3/dax"
)
// Batcher is an interface implemented by anything which can allocate new
// batches.
type Batcher interface {
NewBatch(cfg Config, tbl *dax.Table, fields []*dax.Field) (RecordBatch, error)
}
// Config is the configuration options passed to NewBatch for any implementation
// of the Batcher interface.
type Config struct {
Size int
MaxStaleness time.Duration
}

View file

@ -3,8 +3,8 @@ package batch
import (
"time"
featurebase "github.com/molecula/featurebase/v3"
"github.com/molecula/featurebase/v3/errors"
featurebase "github.com/featurebasedb/featurebase/v3"
"github.com/featurebasedb/featurebase/v3/errors"
)
var (

View file

@ -1,4 +1,5 @@
// Copyright 2021 Molecula Corp. All rights reserved.
// Copyright 2022 Molecula Corp. (DBA FeatureBase).
// SPDX-License-Identifier: Apache-2.0
package egpool
import (
@ -57,11 +58,11 @@ func (eg *Group) err(err error) {
eg.errs = append(eg.errs, err)
}
type ErrPanic struct {
type PanicError struct {
Value interface{}
}
func (p ErrPanic) Error() string {
func (p PanicError) Error() string {
return fmt.Sprintf("panic: %v", p.Value)
}
@ -76,7 +77,7 @@ func (eg *Group) processJobs() {
defer func() {
if !finished {
if p := recover(); p != nil {
eg.err(ErrPanic{p})
eg.err(PanicError{p})
} else {
eg.err(ErrGoexit)
}

View file

@ -1,11 +1,12 @@
// Copyright 2021 Molecula Corp. All rights reserved.
// Copyright 2022 Molecula Corp. (DBA FeatureBase).
// SPDX-License-Identifier: Apache-2.0
package egpool_test
import (
"errors"
"testing"
"github.com/molecula/featurebase/v3/batch/egpool"
"github.com/featurebasedb/featurebase/v3/batch/egpool"
)
func TestEGPool(t *testing.T) {

View file

@ -1 +1,3 @@
// Copyright 2022 Molecula Corp. (DBA FeatureBase).
// SPDX-License-Identifier: Apache-2.0
package batch

View file

@ -1,10 +1,11 @@
// Copyright 2021 Molecula Corp. All rights reserved.
// Copyright 2022 Molecula Corp. (DBA FeatureBase).
// SPDX-License-Identifier: Apache-2.0
package pilosa
import (
"fmt"
"github.com/molecula/featurebase/v3/disco"
"github.com/featurebasedb/featurebase/v3/disco"
"github.com/pkg/errors"
)

5
bsi.go
View file

@ -1,10 +1,11 @@
// Copyright 2021 Molecula Corp. All rights reserved.
// Copyright 2022 Molecula Corp. (DBA FeatureBase).
// SPDX-License-Identifier: Apache-2.0
package pilosa
import (
"math/bits"
"github.com/molecula/featurebase/v3/roaring"
"github.com/featurebasedb/featurebase/v3/roaring"
)
// BSIData contains BSI-structured data.

View file

@ -1,4 +1,5 @@
// Copyright 2021 Molecula Corp. All rights reserved.
// Copyright 2022 Molecula Corp. (DBA FeatureBase).
// SPDX-License-Identifier: Apache-2.0
package pilosa
import (

110
buffer/filebuffer.go Normal file
View file

@ -0,0 +1,110 @@
package buffer
import (
"bytes"
"io"
"io/ioutil"
"os"
"sync"
)
// NewFileBuffer returns a file buffer which will use an in-memory buffer, until `max` bytes have been written, at which point it will write the contents of memory to a file, and continue writing future data to the file.
// The file will be written to `temp` directory. The buffer fulfills the io.Reader and io.Writer interface
func NewFileBuffer(max int, temp string) *FileBuffer {
return &FileBuffer{max: max, tempDir: temp}
}
type FileBuffer struct {
max int
buf bytes.Buffer
file *os.File
tempDir string
reading bool
files []*os.File
mu sync.Mutex
}
func (fb *FileBuffer) Write(p []byte) (n int, err error) {
if fb.reading {
panic("cannot write after read")
}
if fb.file != nil {
return fb.file.Write(p)
}
n, err = fb.buf.Write(p)
if err != nil {
return
}
if fb.buf.Len() > fb.max {
fb.file, err = ioutil.TempFile(fb.tempDir, "filebuffer-")
if err != nil {
return
}
_, err = io.Copy(fb.file, &fb.buf)
fb.buf.Reset()
}
return
}
func (fb *FileBuffer) Len() (int64, error) {
if fb.file == nil {
return int64(fb.buf.Len()), nil
}
fi, err := fb.file.Stat()
if err != nil {
return 0, err
}
return fi.Size(), nil
}
func (fb *FileBuffer) Read(p []byte) (n int, err error) {
if fb.file != nil {
if !fb.reading {
fb.reading = true
_, err = fb.file.Seek(0, 0)
if err != nil {
return
}
}
return fb.file.Read(p)
}
fb.reading = true
return fb.buf.Read(p)
}
func (fb *FileBuffer) Close() error {
if fb.file != nil {
name := fb.file.Name()
if err := fb.file.Close(); err != nil {
return err
}
for _, f := range fb.files {
f.Close()
}
fb.files = fb.files[:0]
fb.file = nil
return os.Remove(name)
}
return nil
}
func (fb *FileBuffer) Reset() error {
fb.mu.Lock()
defer fb.mu.Unlock()
fb.reading = false
fb.buf.Reset()
return fb.Close()
}
func (fb *FileBuffer) NewReader() (io.Reader, error) {
fb.mu.Lock()
defer fb.mu.Unlock()
fb.reading = true
if fb.file == nil {
return bytes.NewReader(fb.buf.Bytes()), nil
}
f, err := os.OpenFile(fb.file.Name(), os.O_RDONLY, 0)
fb.files = append(fb.files, f)
return f, err
}

View file

@ -53,10 +53,10 @@ const PAGE_PREV_POINTER_OFFSET = 12 // offset 12, length 4, end 16
const PAGE_NEXT_POINTER_OFFSET = 16 // offset 16, length 4, end 20
const PAGE_SLOTS_START_OFFSET = 20 // offset 20
// page slots
// PAGE_SLOT_LENGTH is the size of the page slot key/value.
//
// key offset int16 //offset 0, length 2, end 2
// value offset int16 //offset 2, length 2, end 4
// key offset int16 //offset 0, length 2, end 2
// value offset int16 //offset 2, length 2, end 4
const PAGE_SLOT_LENGTH = 4
// Page represents a page on disk

View file

@ -1,4 +1,5 @@
// Copyright 2021 Molecula Corp. All rights reserved.
// Copyright 2022 Molecula Corp. (DBA FeatureBase).
// SPDX-License-Identifier: Apache-2.0
package pilosa
import (
@ -10,8 +11,8 @@ import (
"sync"
"time"
"github.com/molecula/featurebase/v3/lru"
pb "github.com/molecula/featurebase/v3/proto"
"github.com/featurebasedb/featurebase/v3/lru"
pb "github.com/featurebasedb/featurebase/v3/proto"
"github.com/pkg/errors"
)

View file

@ -1,11 +1,12 @@
// Copyright 2021 Molecula Corp. All rights reserved.
// Copyright 2022 Molecula Corp. (DBA FeatureBase).
// SPDX-License-Identifier: Apache-2.0
package pilosa_test
import (
"reflect"
"testing"
"github.com/molecula/featurebase/v3"
pilosa "github.com/featurebasedb/featurebase/v3"
)
// Ensure cache stays constrained to its configured size.
@ -61,7 +62,7 @@ func TestCache_Rank_Dirty(t *testing.T) {
cache.Add(v.ID, v.Count)
}
var got []pair
var got []pair //nolint:prealloc
for _, p := range cache.Top() {
got = append(got, pair(p))
}

View file

@ -1,10 +1,11 @@
// Copyright 2021 Molecula Corp. All rights reserved.
// Copyright 2022 Molecula Corp. (DBA FeatureBase).
// SPDX-License-Identifier: Apache-2.0
package pilosa
import (
"github.com/molecula/featurebase/v3/roaring"
txkey "github.com/molecula/featurebase/v3/short_txkey"
"github.com/molecula/featurebase/v3/vprint"
"github.com/featurebasedb/featurebase/v3/roaring"
txkey "github.com/featurebasedb/featurebase/v3/short_txkey"
"github.com/featurebasedb/featurebase/v3/vprint"
)
// catcher is useful to report error locations with a
@ -123,6 +124,17 @@ func (c *catcherTx) Remove(index, field, view string, shard uint64, a ...uint64)
return c.b.Remove(index, field, view, shard, a...)
}
func (c *catcherTx) Removed(index, field, view string, shard uint64, a ...uint64) (changed []uint64, err error) {
defer func() {
if r := recover(); r != nil {
vprint.AlwaysPrintf("see Removed() PanicOn '%v' at '%v'", r, vprint.Stack())
vprint.PanicOn(r)
}
}()
return c.b.Removed(index, field, view, shard, a...)
}
func (c *catcherTx) Contains(index, field, view string, shard uint64, key uint64) (exists bool, err error) {
defer func() {

15
cli/Makefile Normal file
View file

@ -0,0 +1,15 @@
.PHONY: test testv test-integration testv-integration
GO=go
test:
$(GO) test ./... -short
testv:
$(GO) test -v ./... -short
test-integration:
$(GO) test . -count 1 -timeout 20m -run TestCLIIntegration/$(RUN)
testv-integration:
$(GO) test -v . -count 1 -timeout 20m -run TestCLIIntegration/$(RUN)

8
cli/batch/inserter.go Normal file
View file

@ -0,0 +1,8 @@
package batch
// Inserter can be implemented by anything which can handle a SQL statement
// representing a write operation. An example is `BULK INSERT`. The Insert()
// method on this interface does not return any results other than an error.
type Inserter interface {
Insert(sql string) error
}

215
cli/batch/sql.go Normal file
View file

@ -0,0 +1,215 @@
package batch
import (
"encoding/json"
"fmt"
"strings"
"time"
fbbatch "github.com/featurebasedb/featurebase/v3/batch"
"github.com/featurebasedb/featurebase/v3/dax"
"github.com/featurebasedb/featurebase/v3/errors"
"github.com/featurebasedb/featurebase/v3/pql"
)
// Ensure type implements interface.
var _ fbbatch.Batcher = (*sqlBatcher)(nil)
type sqlBatcher struct {
inserter Inserter
fields []*dax.Field
}
func NewSQLBatcher(i Inserter, flds []*dax.Field) *sqlBatcher {
return &sqlBatcher{
inserter: i,
fields: flds,
}
}
func (b *sqlBatcher) NewBatch(cfg fbbatch.Config, tbl *dax.Table, flds []*dax.Field) (fbbatch.RecordBatch, error) {
fields := flds
if b.fields != nil {
fields = b.fields
}
return &sqlBatch{
table: tbl,
fields: fields,
size: cfg.Size,
maxStaleness: cfg.MaxStaleness,
ids: make([]interface{}, 0, cfg.Size),
rows: make([][]interface{}, 0, cfg.Size),
inserter: b.inserter,
}, nil
}
// Ensure type implements interface.
var _ fbbatch.RecordBatch = (*sqlBatch)(nil)
type sqlBatch struct {
table *dax.Table
fields []*dax.Field
size int
ids []interface{}
rows [][]interface{}
// staleTime tracks the time the first record of the batch was inserted
// plus the maxStaleness, in order to raise ErrBatchNowStale if the
// maxStaleness has elapsed
staleTime time.Time
maxStaleness time.Duration
// inserter handles SQL INSERT statements generated for each batch.
inserter Inserter
}
func (b *sqlBatch) Add(rec fbbatch.Row) error {
// Clear rec.Values and rec.Clears upon return.
defer func() {
for i := range rec.Values {
rec.Values[i] = nil
}
for k := range rec.Clears {
delete(rec.Clears, k)
}
}()
if len(b.ids) == cap(b.ids) {
return fbbatch.ErrBatchAlreadyFull
}
if len(rec.Values) != len(b.fields) {
return errors.Errorf("record needs to match up with batch fields, got %d fields and %d record", len(b.fields), len(rec.Values))
}
// Append the ID to b.ids.
b.ids = append(b.ids, rec.ID)
// Convert decimal fields (which come in as int64, along with the scale in
// field) to pql.Decimal.
for i, fld := range b.fields {
switch b.fields[i].Type {
case dax.BaseTypeDecimal:
if val, ok := rec.Values[i].(int64); ok {
rec.Values[i] = pql.NewDecimal(val, fld.Options.Scale)
}
case dax.BaseTypeTimestamp:
if val, ok := rec.Values[i].(int64); ok {
ts := time.Unix(val, 0)
rec.Values[i] = ts.Format(time.RFC3339)
}
}
}
// Append the record to b.rows.
vals := make([]interface{}, 0, len(rec.Values))
vals = append(vals, rec.Values...)
b.rows = append(b.rows, vals)
// Check for batch full or stale.
if len(b.ids) == cap(b.ids) {
return fbbatch.ErrBatchNowFull
}
if b.maxStaleness != time.Duration(0) { // set maxStaleness to 0 to disable staleness checking
if len(b.ids) == 1 {
b.staleTime = time.Now().Add(b.maxStaleness)
} else if time.Now().After(b.staleTime) {
return fbbatch.ErrBatchNowStale
}
}
return nil
}
func (b *sqlBatch) Import() error {
if len(b.rows) == 0 {
return nil
}
// Construct the BULK INSERT statement based on the table and fields.
sql, err := buildBulkInsert(b.table, b.fields, b.ids, b.rows)
if err != nil {
return errors.Wrap(err, "building bulk insert statement")
}
// Reset batch data.
b.reset()
// Submit the SQL statement.
return b.inserter.Insert(sql)
}
func (b *sqlBatch) reset() {
b.ids = b.ids[:0]
b.rows = b.rows[:0]
}
func (b *sqlBatch) Len() int {
return len(b.rows)
}
func (b *sqlBatch) Flush() error {
return nil
}
func buildBulkInsert(tbl *dax.Table, fields []*dax.Field, ids []interface{}, rows [][]interface{}) (string, error) {
// Validation.
if tbl.Name == "" {
return "", errors.New(errors.ErrUncoded, "table name is required")
} else if len(fields) == 0 {
return "", errors.New(errors.ErrUncoded, "at least one field is required")
}
var sb strings.Builder
sb.WriteString(`BULK INSERT INTO `)
sb.WriteString(string(tbl.Name))
sb.WriteString(` (_id,`)
flds := make([]string, 0, len(fields))
maps := make([]string, 0, len(fields))
for i := range fields {
flds = append(flds, string(fields[i].Name))
maps = append(maps, fmt.Sprintf("'$.col_%d' %s", i, fields[i].FullType()))
}
// Fields
sb.WriteString(strings.Join(flds, ","))
// MAP
keyType := dax.BaseTypeID
if tbl.StringKeys() {
keyType = dax.BaseTypeString
}
sb.WriteString(`) MAP ('$._id' `)
sb.WriteString(keyType)
sb.WriteString(`,`)
sb.WriteString(strings.Join(maps, ","))
sb.WriteString(`) FROM x'`)
// Row values.
// m is a map representing a single row to be marshalled and added to the
// bulk insert as one line in the NDJSON payload. We re-use the map for each
// row.
m := make(map[string]interface{})
for i := range rows {
// Write the ID value.
m[string(dax.PrimaryKeyFieldName)] = ids[i]
// Write the rest of the data values.
for col := range rows[i] {
m[fmt.Sprintf("col_%d", col)] = rows[i][col]
}
// Marshal the map to json and add to the sql statement.
if j, err := json.Marshal(m); err != nil {
return "", errors.Wrap(err, "marshalling row to json")
} else {
sb.Write(j)
sb.WriteString("\n")
}
}
// WITH
sb.WriteString(fmt.Sprintf(`' WITH BATCHSIZE %d FORMAT 'NDJSON' INPUT 'STREAM'`, len(rows)))
return sb.String(), nil
}

47
cli/batch/sql_test.go Normal file
View file

@ -0,0 +1,47 @@
package batch
import (
"testing"
"github.com/featurebasedb/featurebase/v3/dax"
"github.com/stretchr/testify/assert"
)
func TestBatchSQL(t *testing.T) {
tbl := &dax.Table{
Name: "foo",
}
fields := []*dax.Field{
{
Name: "name",
Type: dax.BaseTypeString,
},
{
Name: "age",
Type: dax.BaseTypeInt,
},
}
ids := []interface{}{
0, 1, 2,
}
rows := [][]interface{}{
{
[]interface{}{"Alice", int64(11)},
},
{
[]interface{}{"Bob", int64(22)},
},
{
[]interface{}{"Carl,Comma", int64(33)},
},
}
s, err := buildBulkInsert(tbl, fields, ids, rows)
assert.NoError(t, err)
exp := `BULK INSERT INTO foo (_id,name,age) MAP ('$._id' id,'$.col_0' string,'$.col_1' int) FROM x'{"_id":0,"col_0":["Alice",11]}
{"_id":1,"col_0":["Bob",22]}
{"_id":2,"col_0":["Carl,Comma",33]}
' WITH BATCHSIZE 3 FORMAT 'NDJSON' INPUT 'STREAM'`
assert.Equal(t, exp, s)
}

91
cli/buffer.go Normal file
View file

@ -0,0 +1,91 @@
package cli
import (
"io"
"strings"
"github.com/featurebasedb/featurebase/v3/errors"
)
// buffer is a query buffer for SQL statements. Note that this is not a query
// buffer as you would find on a database server (buffering query results).
// Rather, this buffers the working SQL statement. The buffer has two
// components: the buffer of query parts making up the working, incomplete SQL
// statement, and the last completed SQL statement submitted to the Queryer.
type buffer struct {
parts []queryPart
lastQuery query
hasBatchFile bool
}
func newBuffer() *buffer {
return &buffer{}
}
// addPart adds the given queryPart to the buffer. If the part is of type
// `partTerminator` (which is generally singified in the CLI by a ";"), the
// buffer will finalize the query and return it. In all other cases, the
// returned query is nil.
func (b *buffer) addPart(part queryPart) (query, error) {
// Check for part type compatibility. For example, multiple batchFile parts
// are not allowed in the same query.
switch part.(type) {
case *partBatchFile:
if b.hasBatchFile {
return nil, errors.Errorf("multiple batch files in one query is not supported")
}
b.hasBatchFile = true
case *partTerminator:
return b.finalize(), nil
}
b.parts = append(b.parts, part)
return nil, nil
}
// finalize copies the contents (queryParts) of buffer to lastQuery and then
// resets the buffer. It returns the query that was finalized.
func (b *buffer) finalize() query {
q := make(query, len(b.parts))
copy(q, b.parts)
b.lastQuery = q
b.reset()
return q
}
// print returns the contents of the buffer as a string. This is generally used
// to visually inspect the state of the buffer (for example, when a user issues
// a `\p` meta-command in the CLI).
func (b *buffer) print() string {
if len(b.parts) > 0 {
return query(b.parts).String()
} else if b.lastQuery != nil {
return b.lastQuery.String() + ";"
}
return "Query buffer is empty."
}
// reset clears the buffer. It returns a message which may optionally be used to
// display to a user.
func (b *buffer) reset() string {
b.parts = b.parts[:0]
b.hasBatchFile = false
return "Query buffer reset (cleared)."
}
func (b *buffer) Reader() io.Reader {
if len(b.parts) > 0 {
return query(b.parts).Reader()
} else if b.lastQuery != nil {
r := b.lastQuery.Reader()
// TODO(tlt): terminating the query here results in a line feed just
// before the semi-colon (for example, when you print out the query
// buffer using `\w [FILE]`). The removal and re-introduction of line
// feeds is kind of a mess.
term := strings.NewReader(";")
return io.MultiReader(r, term)
}
return strings.NewReader("")
}

1046
cli/cli.go

File diff suppressed because it is too large Load diff

257
cli/cli_integration_test.go Normal file
View file

@ -0,0 +1,257 @@
package cli_test
import (
"bufio"
"context"
"fmt"
"os"
"strings"
"testing"
"time"
"github.com/featurebasedb/featurebase/v3/cli"
"github.com/featurebasedb/featurebase/v3/dax/server/test"
"github.com/featurebasedb/featurebase/v3/logger"
"github.com/stretchr/testify/require"
)
func TestCLIIntegration(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test")
}
ctx := context.Background()
t.Run("Stubbed Framework", func(t *testing.T) {
mc := test.MustRunManagedCommand(t)
defer mc.Close()
addr := mc.Address()
capture := newCapture(t)
comparer := newComparer(t)
comparer.run()
fbsql := cli.NewCommand(logger.StderrLogger)
fbsql.SetStdin(capture)
fbsql.SetStdout(comparer)
fbsql.SetStderr(comparer)
fbsql.Config = &cli.Config{
Host: addr.Host(),
Port: fmt.Sprintf("%d", addr.Port()),
}
// Run fbsql in a goroutine so we can continue to send it commands
// below.
didQuit := make(chan struct{})
go func() {
require.NoError(t, fbsql.Run(ctx))
close(didQuit)
}()
// testFiles reference files located in the cli/testdata directory. All
// tests should be placed there; other than adding another test file to
// this list, you probably shouldn't be editing this file unless you are
// trying to modify the way the test framework itself works.
testFiles := []string{
"setup",
"database",
"table",
// the tests below may be dependent on the previous tests, which do
// setup and some shared database and table creation.
"query_buffer",
// meta commands
"meta_bang",
"meta_cd",
"meta_echo",
"meta_describe",
"meta_file",
"meta_pset_border",
"meta_pset_expanded",
"meta_pset_format_csv",
"meta_pset_tuples_only",
"meta_include",
"meta_output",
"meta_set",
"meta_timing",
"meta_write",
}
for _, testFile := range testFiles {
t.Run(testFile, func(t *testing.T) {
f, err := os.Open("testdata/" + testFile)
require.NoError(t, err)
scanner := bufio.NewScanner(f)
var lineNo int
for scanner.Scan() {
line := scanner.Text()
lineNo++
// Empty lines and comments (//) are ignored.
if line == "" {
continue
} else if strings.HasPrefix(line, "//") {
continue
}
parts := strings.SplitN(line, ":", 2)
switch parts[0] {
case "SEND":
v := ""
if len(parts) == 2 {
v = parts[1]
}
capture.sendLine(v)
case "EXPECT":
v := ""
if len(parts) == 2 {
v = parts[1]
}
comparer.expectLine(v, testFile, lineNo)
case "EXPECTCOMP":
if len(parts) == 2 {
comps := strings.SplitN(parts[1], ":", 2)
v := ""
if len(comps) == 2 {
v = comps[1]
}
comparer.expectLineComp(comparator(comps[0]), v, testFile, lineNo)
} else {
t.Errorf("unexpected line: %s[%d]:%s", testFile, lineNo, line)
}
default:
t.Errorf("unexpected line: %s[%d]:%s", testFile, lineNo, line)
}
}
require.NoError(t, scanner.Err())
})
}
// End with quit to ensure that fbsql closes without error.
capture.sendLine(`\q`)
// Ensure fbsql quits cleanly.
select {
case <-didQuit:
case <-time.After(time.Second):
t.Fatalf("expected fbsql to quit")
}
})
}
// compare is used to compare fbsql output written to its Stdout with expected
// lines.
type comparer struct {
t *testing.T
out chan byte
outline chan []byte
exp chan []byte
}
func newComparer(t *testing.T) *comparer {
return &comparer{
t: t,
out: make(chan byte, 1024),
outline: make(chan []byte, 128),
exp: make(chan []byte, 1024),
}
}
func (c *comparer) run() {
// Read bytes off output, and for every line (designated by a line feed "\n"),
// push the line onto the outline channel.
go func() {
var line []byte
for {
b := <-c.out
if b == byte('\n') {
c.outline <- line
line = []byte{}
continue
}
line = append(line, b)
}
}()
}
type comparator string
const (
compEquals = "Equals"
compHasPrefix = "HasPrefix"
compWithFormat = "WithFormat"
)
// expectLine is a convenience method which calls expectLineComp with the compEq
// comparator and the given line.
func (c *comparer) expectLine(line string, fileName string, lineNo int) {
c.expectLineComp(compEquals, line, fileName, lineNo)
}
// expectLineComp reads the next line from the outline channel and compares it
// with the given `line`. A comparator can be provided to inform how the lines
// should be compared (for example, the compHasPrefix comparator will just
// compare the beginning part of the outline).
func (c *comparer) expectLineComp(comp comparator, line string, fileName string, lineNo int) {
var outline []byte
select {
case outline = <-c.outline:
case <-time.After(10 * time.Second):
// TODO(tlt): this is 10 seconds to account for the fb_views creation on
// a local mac. This should really be something like 2 seconds. Put this
// back to 2 once fb_views issue is addressed.
c.t.Fatalf("expected output line %s[%d]: >%s<", fileName, lineNo, line)
}
// msg is included in any require which fails.
msg := []interface{}{"exp: %s[%d], got: >%s<", fileName, lineNo, outline}
switch comp {
case compEquals:
require.Equal(c.t, []byte(line), outline, msg...)
case compHasPrefix:
require.True(c.t, strings.HasPrefix(string(outline), line), msg...)
case compWithFormat:
require.True(c.t, compareByteSlices(outline, []byte(line)), msg...)
default:
c.t.Fatalf("invalid comparator: %s", comp)
}
}
func (c *comparer) Write(b []byte) (n int, err error) {
for i := range b {
c.out <- b[i]
}
return len(b), err
}
// compareByteSlices compares a byte slice s with another byte slice format and
// returns true if they are the same. It will accept underscore as a
// single-character wildcard anywhere in slice format.
func compareByteSlices(s, format []byte) bool {
// Replace some helpers in format before comparing.
f := string(format)
f = strings.ReplaceAll(f, `{uuid}`, `________-____-____-____-____________`)
f = strings.ReplaceAll(f, `{timestamp}`, `____-__-__T__:__:__Z`)
format = []byte(f)
if len(s) != len(format) {
return false
}
for i := range s {
if format[i] == '_' {
continue
}
if s[i] != format[i] {
// log.Printf("DEBUG: characters differ: (%d): '%v' != '%v'", i, s[i], format[i])
return false
}
}
return true
}

View file

@ -8,9 +8,9 @@ import (
"testing"
"time"
featurebase "github.com/molecula/featurebase/v3"
"github.com/molecula/featurebase/v3/cli"
"github.com/molecula/featurebase/v3/logger"
featurebase "github.com/featurebasedb/featurebase/v3"
"github.com/featurebasedb/featurebase/v3/cli"
"github.com/featurebasedb/featurebase/v3/logger"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)
@ -21,9 +21,9 @@ func TestCLI(t *testing.T) {
capture := newCapture(t)
cli := cli.NewCLICommand(logger.StderrLogger)
cli.Stdin = capture
cli.Stdout = capture
cli := cli.NewCommand(logger.StderrLogger)
cli.SetStdin(capture)
cli.SetStdout(capture)
cli.Queryer = capture
go func() {
@ -33,38 +33,32 @@ func TestCLI(t *testing.T) {
none := []string{}
// One statement, one line.
capture.Assert("one;", []string{`one`})
capture.Assert("one;", []string{"one\n"})
// One statement, multiple lines.
capture.Assert("one", none)
capture.Assert(" two ", none)
capture.Assert("three;", []string{`one
two
three`})
capture.Assert("three;", []string{"one\ntwo\nthree\n"})
// Multiple statements, one line.
capture.Assert("foo; bar;", []string{`foo`, `bar`})
capture.Assert("foo; bar;", []string{"foo\n", "bar\n"})
// Multiple statements, multiple lines.
capture.Assert("a1", none)
capture.Assert("a2; b1", []string{`a1
a2`})
capture.Assert("b2;", []string{`b1
b2`})
capture.Assert("a2; b1", []string{"a1\na2\n"})
capture.Assert("b2;", []string{"b1\nb2\n"})
// Blank lines.
capture.Assert("one", none)
capture.Assert("", none)
capture.Assert("three;", []string{`one
three`})
capture.Assert("three;", []string{"one\nthree\n"})
// Just a semi-colon.
capture.Assert(";", none)
capture.Assert(";", []string{""})
// Multi-line with just a semi-colon.
capture.Assert("one", none)
capture.Assert(";", []string{`one`})
capture.Assert(";", []string{"one\n"})
// Ensure a clean exit with no errors.
assert.NoError(t, capture.Exit())
@ -80,7 +74,7 @@ var _ cli.Queryer = (*capture)(nil)
// capture implements the various CLI interfaces in order to capture test input
// and submit it as though that input were being read from the command line. It
// also captures calls made to the Queryer.Query method and ensures the sql the
// also captures calls made to the Queryer.Query method and ensures the sql they
// contain is expected.
type capture struct {
t *testing.T
@ -111,7 +105,7 @@ func newCapture(t *testing.T) *capture {
}
func (c *capture) Exit() error {
c.sendLine("exit")
c.sendLine(`\q`)
c.mu.RLock()
defer c.mu.RUnlock()
return c.err
@ -183,9 +177,15 @@ func (c *capture) Write(b []byte) (n int, err error) {
// Query is called by the CLI command once a full SQL statement is received
// (signified by the terminator: `;`).
func (c *capture) Query(org, db, sql string) (*featurebase.WireQueryResponse, error) {
func (c *capture) Query(org string, db string, sql io.Reader) (*featurebase.WireQueryResponse, error) {
tmpBuf := new(strings.Builder)
_, err := io.Copy(tmpBuf, sql)
if err != nil {
return nil, err
}
c.mu.Lock()
c.sqls = append(c.sqls, sql)
c.sqls = append(c.sqls, tmpBuf.String())
c.mu.Unlock()
select {

31
cli/config.go Normal file
View file

@ -0,0 +1,31 @@
package cli
// Config represents the configuration for the command.
type Config struct {
Host string `json:"host"`
Port string `json:"port"`
OrganizationID string `json:"org-id"`
Database string `json:"db"`
// CloudAuth
CloudAuth CloudAuthConfig `json:"cloud-auth"`
// Kafka
KafkaConfig string `json:"kafka-config"`
HistoryPath string `json:"history-path"`
// CSV (Comma-Separated Values) table output mode.
CSV bool `json:"csv"`
// PSet takes one or more pset arguments of the form: `--pset=VAR[=ARG]`.
PSets []string `json:"pset"`
}
type CloudAuthConfig struct {
ClientID string `json:"client-id"`
Region string `json:"region"`
Email string `json:"email"`
Password string `json:"password"`
}

16
cli/errors.go Normal file
View file

@ -0,0 +1,16 @@
package cli
import (
"github.com/featurebasedb/featurebase/v3/errors"
)
const (
ErrOrganizationRequired errors.Code = "OrganizationRequired"
)
func NewErrOrganizationRequired() error {
return errors.New(
ErrOrganizationRequired,
"organization required",
)
}

View file

@ -1,7 +1,6 @@
package fbcloud
import (
"bytes"
"encoding/json"
"fmt"
"io"
@ -9,7 +8,7 @@ import (
"strings"
"time"
featurebase "github.com/molecula/featurebase/v3"
featurebase "github.com/featurebasedb/featurebase/v3"
"github.com/pkg/errors"
)
@ -40,37 +39,26 @@ func (cq *Queryer) tokenRefresh() error {
return nil
}
type tokenizedSQL struct {
Language string `json:"language"`
Statement string `json:"statement"`
}
// Query issues a SQL query formatted for the FeatureBase cloud query endpoint.
func (cq *Queryer) Query(org, db, sql string) (*featurebase.WireQueryResponse, error) {
func (cq *Queryer) Query(org string, db string, sql io.Reader) (*featurebase.WireQueryResponse, error) {
if time.Since(cq.lastRefresh) > TokenRefreshTimeout {
if err := cq.tokenRefresh(); err != nil {
return nil, errors.Wrap(err, "refreshing token")
}
}
url := fmt.Sprintf("%s/v2/databases/%s/query/sql", cq.Host, db)
sqlReq := &tokenizedSQL{
Language: "sql",
Statement: sql,
}
var buf bytes.Buffer
if err := json.NewEncoder(&buf).Encode(sqlReq); err != nil {
return nil, errors.Wrapf(err, "encoding sql request: %s", sql)
url := fmt.Sprintf("%s/databases/%s/sql", cq.Host, db)
if db == "" {
url = fmt.Sprintf("%s/sql", cq.Host)
}
client := &http.Client{
Timeout: time.Second * 30,
}
req, err := http.NewRequest(http.MethodPost, url, &buf)
req, err := http.NewRequest(http.MethodPost, url, sql)
if err != nil {
return nil, errors.Wrap(err, "creating new post request")
}
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Content-Type", "text/plain")
req.Header.Add("Authorization", cq.token)
var resp *http.Response
@ -141,7 +129,3 @@ func (cq *Queryer) HTTPRequest(method, path, body string, v interface{}) ([]byte
return bodbytes, nil
}
type cloudResponse struct {
Results featurebase.WireQueryResponse `json:"results"`
}

70
cli/kafka.go Normal file
View file

@ -0,0 +1,70 @@
package cli
import (
"fmt"
"github.com/featurebasedb/featurebase/v3/cli/batch"
"github.com/featurebasedb/featurebase/v3/cli/kafka"
"github.com/featurebasedb/featurebase/v3/errors"
"github.com/spf13/viper"
)
func (cmd *Command) newKafkaRunner(cfgFile string) (*kafka.Runner, error) {
// Read the kafka config file.
v := viper.New()
v.SetConfigFile(cfgFile)
v.SetConfigType("toml")
err := v.ReadInConfig()
if err != nil {
return nil, fmt.Errorf("error reading configuration file '%s': %v", cfgFile, err)
}
cfg := kafka.Config{}
if err := v.Unmarshal(&cfg); err != nil {
return nil, errors.Wrap(err, "unmarshalling config")
}
if err := kafka.ValidateConfig(cfg); err != nil {
return nil, errors.Wrap(err, "validating config")
}
// Create a new config with defaults.
// Look up fields based on table provided in the config.
wqr, err := cmd.executeQuery(newRawQuery("SHOW COLUMNS FROM " + cfg.Table))
if err != nil {
return nil, errors.Wrap(err, "executing query")
}
scr, err := wqr.ShowColumnsResponse()
if err != nil {
return nil, errors.Wrap(err, "getting show columns from wire query response")
}
// If no fields were provided in the config, use the fields defined on the
// table and assume a 1-to-1 mapping of source to destination.
if len(cfg.Fields) == 0 {
cfg.Fields = kafka.FieldsToConfig(scr.Fields)
} else {
cfg.Fields, err = kafka.CheckFieldCompatibility(cfg.Fields, scr)
if err != nil {
return nil, errors.Wrap(err, "validating config fields")
}
}
idkCfg, err := kafka.ConvertConfig(cfg)
if err != nil {
return nil, errors.Wrap(err, "cleaning config")
}
flds, err := kafka.ConfigToFields(cfg)
if err != nil {
return nil, errors.Wrap(err, "getting fields from config")
}
return kafka.NewRunner(
idkCfg,
batch.NewSQLBatcher(cmd, flds),
cmd.stderr,
), nil
}

251
cli/kafka/config.go Normal file
View file

@ -0,0 +1,251 @@
package kafka
import (
"fmt"
"time"
featurebase "github.com/featurebasedb/featurebase/v3"
"github.com/featurebasedb/featurebase/v3/dax"
"github.com/featurebasedb/featurebase/v3/idk"
"github.com/pkg/errors"
)
// Config is the user-facing configuration for kafka support in the CLI. This is
// unmarshalled from the the toml config file supplied by the user.
type Config struct {
Hosts []string `mapstructure:"hosts" help:"Kafka hosts."`
Group string `mapstructure:"group" help:"Kafka group."`
Topics []string `mapstructure:"topics" help:"Kafka topics to read from."`
BatchSize int `mapstructure:"batch-size" help:"Batch size."`
BatchMaxStaleness time.Duration `mapstructure:"batch-max-staleness" help:"Maximum length of time that the oldest record in a batch can exist before flushing the batch. Note that this can potentially stack with timeouts waiting for the source."`
Timeout time.Duration `mapstructure:"timeout" help:"Time to wait for more records from Kafka before flushing a batch. 0 to disable."`
Table string `mapstructure:"table" help:"Destination table name."`
Fields []Field `mapstructure:"fields"`
}
// Field is a user-facing configuration field.
type Field struct {
Name string `mapstructure:"name"`
SourceType string `mapstructure:"source-type"`
SourcePath []string `mapstructure:"source-path"`
PrimaryKey bool `mapstructure:"primary-key"`
}
// ConfigForIDK represents Config converted to values suitable for IDK. In
// particular, the idk.RawField is used in parsing the schema in IDK.
type ConfigForIDK struct {
Hosts []string
Group string
Topics []string
BatchSize int
BatchMaxStaleness time.Duration
Timeout time.Duration
Table string
IDField string
Fields []idk.RawField
}
// ValidateConfig validates the config is usable.
func ValidateConfig(c Config) error {
if c.Table == "" {
return errors.Errorf("table is required")
} else if len(c.Topics) == 0 {
return errors.Errorf("at least one topic is required")
} else if len(c.Fields) > 0 {
// We only need to do these checks if any fields are specified at all.
// If no fields are specified, that's ok because then we default to
// using fields based off the existing table.
if len(c.Fields) < 2 {
return errors.Errorf("at least two fields are required (one should be a primary key)")
} else {
var found int
for i := range c.Fields {
if c.Fields[i].PrimaryKey {
found++
}
if c.Fields[i].Name == "" {
return errors.Errorf("a name attribute (which isn't equal to \"\") should exist for all fields")
}
}
if found != 1 {
return errors.Errorf("exactly one primary key field is required")
}
}
}
return nil
}
// ConvertConfig converts a Config to one that suitable for IDK.
func ConvertConfig(c Config) (ConfigForIDK, error) {
// Set a default kafka host in case one isn't provided.
hosts := []string{"localhost:9092"}
if len(c.Hosts) > 0 {
hosts = c.Hosts
}
// Copy all the shared members from Config to ConfigForIDK.
out := ConfigForIDK{
Hosts: hosts,
Group: c.Group,
Topics: c.Topics,
BatchSize: c.BatchSize,
BatchMaxStaleness: c.BatchMaxStaleness,
Timeout: c.Timeout,
Table: c.Table,
}
if len(c.Fields) == 0 {
return out, errors.New("fields cannot be empty")
}
// rawFields wil be the same as c.Fields, but possibly enhanced.
rawFields := make([]idk.RawField, 0, len(c.Fields))
var foundPK bool
for _, fld := range c.Fields {
if fld.PrimaryKey {
out.IDField = fld.Name
foundPK = true
}
typ, quals, err := dax.SplitFieldType(fld.SourceType)
if err != nil {
return out, errors.Wrap(err, "getting base type")
}
rawFld := idk.RawField{
Name: fld.Name,
Type: string(typ),
Path: fld.SourcePath,
}
// If a SourcePath wasn't provided, default to using the field name.
if len(rawFld.Path) == 0 {
rawFld.Path = []string{fld.Name}
}
switch typ {
case dax.BaseTypeInt:
// We don't have to handle min/max because we don't create the table.
case dax.BaseTypeDecimal:
if len(quals) != 1 {
return out, errors.Errorf("expected decimal scale")
}
rawFld.Config = []byte(fmt.Sprintf(`{"scale":%d}`, quals[0]))
case dax.BaseTypeID:
rawFld.Config = []byte("{\"mutex\":true}")
case dax.BaseTypeIDSet:
rawFld.Type = "ids"
case dax.BaseTypeString:
rawFld.Config = []byte("{\"mutex\":true}")
case dax.BaseTypeStringSet:
rawFld.Type = "strings"
case dax.BaseTypeTimestamp:
// No timestamp options are handled for now.
}
rawFields = append(rawFields, rawFld)
}
if !foundPK {
return out, errors.New("primary-key not found in fields")
}
out.Fields = rawFields
return out, nil
}
// ConfigToFields returns a list of *dax.Field based on the IDField and Fields
// in the Config.
func ConfigToFields(c Config) ([]*dax.Field, error) {
// We don't know if a primary key will be found, so we can't set the
// capacity to `len(c.Fields)-1`.
out := make([]*dax.Field, 0, len(c.Fields))
for _, fld := range c.Fields {
if fld.PrimaryKey {
continue
}
typ, quals, err := dax.SplitFieldType(fld.SourceType)
if err != nil {
return nil, errors.Wrap(err, "splitting field type")
}
dfld := &dax.Field{
Name: dax.FieldName(fld.Name),
Type: typ,
}
switch typ {
case dax.BaseTypeDecimal:
if len(quals) != 1 {
return nil, errors.Errorf("expected decimal scale")
}
scale, ok := quals[0].(int64)
if !ok {
return nil, errors.Errorf("invalid decimal scale: %v", quals[0])
}
dfld.Options.Scale = scale
}
out = append(out, dfld)
}
return out, nil
}
// FieldsToConfig returns a Config.Fields based on a list of *dax.Field.
func FieldsToConfig(flds []*dax.Field) []Field {
out := make([]Field, 0, len(flds))
for _, fld := range flds {
out = append(out, Field{
Name: string(fld.Name),
SourceType: fld.FullType(),
PrimaryKey: fld.IsPrimaryKey(),
})
}
return out
}
// CheckFieldCompatibility ensures that the fields provided in the kafka config
// are compatible with the fields in the existing table. It returns a copy of
// the kafka config fields with empty values defaulted to the table field
// configuration.
func CheckFieldCompatibility(cflds []Field, scr *featurebase.ShowColumnsResponse) ([]Field, error) {
out := make([]Field, len(cflds))
for i, cfld := range cflds {
out[i] = cfld
cfldName := dax.FieldName(cfld.Name)
// Primary key field.
if cfld.PrimaryKey {
f := scr.Field(dax.PrimaryKeyFieldName)
if f == nil {
return nil, dax.NewErrFieldDoesNotExist(dax.PrimaryKeyFieldName) // It should be impossible to hit this.
}
if out[i].SourceType == "" {
if f.StringKeys() {
out[i].SourceType = dax.BaseTypeString
} else {
out[i].SourceType = dax.BaseTypeID
}
}
continue
}
// Non primary key fields.
if cfldName == dax.PrimaryKeyFieldName {
return nil, errors.Errorf("field named '%s' must be a primary key", dax.PrimaryKeyFieldName)
}
f := scr.Field(cfldName)
if f == nil {
return nil, dax.NewErrFieldDoesNotExist(cfldName)
}
if out[i].SourceType == "" {
out[i].SourceType = f.FullType()
}
}
return out, nil
}

67
cli/kafka/runner.go Normal file
View file

@ -0,0 +1,67 @@
package kafka
import (
"io"
"time"
fbbatch "github.com/featurebasedb/featurebase/v3/batch"
"github.com/featurebasedb/featurebase/v3/errors"
"github.com/featurebasedb/featurebase/v3/idk"
"github.com/featurebasedb/featurebase/v3/idk/kafka_static"
"github.com/featurebasedb/featurebase/v3/logger"
)
// Runner is a CLI-specific kafka consumer. It's similar to
// idk.kafka_static.Main in that it embeds idk.Main and contains additional
// functionality specific to its use case.
type Runner struct {
idk.Main `flag:"!embed"`
KafkaHosts []string `help:"Comma separated list of host:port pairs for Kafka."`
Group string `help:"Kafka group."`
Topics []string `help:"Kafka topics to read from."`
Timeout time.Duration `help:"Time to wait for more records from Kafka before flushing a batch. 0 to disable."`
Header []idk.RawField `help:"Header configuration."`
}
func NewRunner(cfg ConfigForIDK, batcher fbbatch.Batcher, logWriter io.Writer) *Runner {
idkMain := idk.NewMain()
idkMain.IDField = cfg.IDField
idkMain.Index = cfg.Table
idkMain.Batcher = batcher
idkMain.BatchSize = cfg.BatchSize
idkMain.BatchMaxStaleness = cfg.BatchMaxStaleness
idkMain.SetBasic()
idkMain.SetLog(logger.NewStandardLogger(logWriter))
kr := &Runner{
Main: *idkMain,
KafkaHosts: cfg.Hosts,
Group: cfg.Group,
Topics: cfg.Topics,
Header: cfg.Fields,
Timeout: cfg.Timeout,
}
kr.OffsetMode = true
kr.Main.Namespace = "cli_kafka_runner"
kr.Main.Pprof = "" // don't initialize pprof until we actually use it in tests
kr.NewSource = func() (idk.Source, error) {
source := kafka_static.NewSource()
source.Hosts = kr.KafkaHosts
source.Group = kr.Group
source.Topics = kr.Topics
source.Log = kr.Main.Log()
// source.TLS = m.KafkaTLS
source.Timeout = kr.Timeout
// source.SkipOld = m.SkipOld
source.HeaderFields = kr.Header
// source.S3Region = m.S3Region
// source.AllowMissingFields = m.AllowMissingFields
err := source.Open()
if err != nil {
return nil, errors.Wrap(err, "opening source")
}
return source, nil
}
return kr
}

1166
cli/meta.go Normal file

File diff suppressed because it is too large Load diff

136
cli/parts.go Normal file
View file

@ -0,0 +1,136 @@
package cli
import (
"fmt"
"io"
"os"
"strings"
)
// query is a collection of queryParts which, when applied together, make up an
// executable SQL query.
type query []queryPart
func (q query) String() string {
var sb strings.Builder
for i := range q {
sb.WriteString(q[i].String())
if i < len(q)-1 {
sb.WriteRune('\n')
}
}
return sb.String()
}
// Reader returns the query as an io.Reader so that it can be passed to, for
// example, http.Post().
func (q query) Reader() io.Reader {
readers := make([]io.Reader, 0, len(q))
for i := range q {
readers = append(readers, q[i].Reader())
}
return io.MultiReader(readers...)
}
// queryPart is an interface representing anything which can use to build up a
// query.
type queryPart interface {
fmt.Stringer
Reader() io.Reader
}
func newRawQuery(s string) query {
return []queryPart{
newPartRaw(s),
}
}
// ////////////////////////////////////////////////////////////////////////////
// raw
// ////////////////////////////////////////////////////////////////////////////
// Ensure type implements interface.
var _ queryPart = (*partRaw)(nil)
type partRaw struct {
raw string
}
func newPartRaw(s string) *partRaw {
return &partRaw{
raw: s,
}
}
func (p *partRaw) Reader() io.Reader {
return strings.NewReader(p.raw + "\n")
}
func (p *partRaw) String() string {
return p.raw
}
// ////////////////////////////////////////////////////////////////////////////
// file
// ////////////////////////////////////////////////////////////////////////////
// Ensure type implements interface.
var _ queryPart = (*partFile)(nil)
type partFile struct {
file *os.File
}
func newPartFile(f *os.File) *partFile {
return &partFile{
file: f,
}
}
func (p *partFile) Reader() io.Reader {
return p.file
}
func (p *partFile) String() string {
return fmt.Sprintf("[file: %s]", p.file.Name())
}
// ////////////////////////////////////////////////////////////////////////////
// batch file
// ////////////////////////////////////////////////////////////////////////////
// Ensure type implements interface.
var _ queryPart = (*partBatchFile)(nil)
type partBatchFile struct {
file *os.File
}
func (p *partBatchFile) Reader() io.Reader {
return p.file
}
func (p *partBatchFile) String() string {
return p.file.Name()
}
// ////////////////////////////////////////////////////////////////////////////
// terminator (i.e. ";")
// ////////////////////////////////////////////////////////////////////////////
// Ensure type implements interface.
var _ queryPart = (*partTerminator)(nil)
type partTerminator struct{}
func newPartTerminator() *partTerminator {
return &partTerminator{}
}
func (p *partTerminator) Reader() io.Reader {
return nil
}
func (p *partTerminator) String() string {
return terminationChar
}

View file

@ -1,20 +1,26 @@
package cli
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
featurebase "github.com/molecula/featurebase/v3"
"github.com/molecula/featurebase/v3/dax"
queryerhttp "github.com/molecula/featurebase/v3/dax/queryer/http"
featurebase "github.com/featurebasedb/featurebase/v3"
"github.com/pkg/errors"
)
type Queryer interface {
Query(org, db, sql string) (*featurebase.WireQueryResponse, error)
Query(org string, db string, sql io.Reader) (*featurebase.WireQueryResponse, error)
}
// Ensure type implements interface.
var _ Queryer = (*nopQueryer)(nil)
type nopQueryer struct{}
func (qryr *nopQueryer) Query(org string, db string, sql io.Reader) (*featurebase.WireQueryResponse, error) {
return nil, errors.Errorf("no-op queryer")
}
// Ensure type implements interface.
@ -27,13 +33,10 @@ type standardQueryer struct {
Port string
}
func (qryr *standardQueryer) Query(org, db, sql string) (*featurebase.WireQueryResponse, error) {
buf := bytes.Buffer{}
func (qryr *standardQueryer) Query(org string, db string, sql io.Reader) (*featurebase.WireQueryResponse, error) {
url := fmt.Sprintf("%s/sql", hostPort(qryr.Host, qryr.Port))
buf.Write([]byte(sql))
resp, err := http.Post(url, "application/json", &buf)
resp, err := http.Post(url, "application/json", sql)
if err != nil {
return nil, errors.Wrapf(err, "posting query")
}
@ -42,8 +45,10 @@ func (qryr *standardQueryer) Query(org, db, sql string) (*featurebase.WireQueryR
if err != nil {
return nil, errors.Wrap(err, "reading response")
}
sqlResponse := &featurebase.WireQueryResponse{}
// TODO(tlt): switch this back once all responses are typed
// TODO(twg) 2023/03/01 using json.Number to decode large ints so care must be made
// if err := json.Unmarshal(fullbod, sqlResponse); err != nil {
if err := sqlResponse.UnmarshalJSONTyped(fullbod, true); err != nil {
return nil, errors.Wrapf(err, "unmarshaling query response, body:\n'%s'\n", fullbod)
@ -53,32 +58,39 @@ func (qryr *standardQueryer) Query(org, db, sql string) (*featurebase.WireQueryR
}
// Ensure type implements interface.
var _ Queryer = (*daxQueryer)(nil)
var _ Queryer = (*serverlessQueryer)(nil)
// daxQueryer is similar to the standardQueryer except that it hits a different
// endpoint, and its payload is a json object which includes, in addition to the
// sql statement, things like org and db.
type daxQueryer struct {
// serverlessQueryer is similar to the standardQueryer except that it hits a
// different endpoint, and its payload is database-aware.
type serverlessQueryer struct {
Host string
Port string
}
func (qryr *daxQueryer) Query(org, db, sql string) (*featurebase.WireQueryResponse, error) {
buf := bytes.Buffer{}
url := fmt.Sprintf("%s/queryer/sql", hostPort(qryr.Host, qryr.Port))
sqlReq := &queryerhttp.SQLRequest{
OrganizationID: dax.OrganizationID(org),
DatabaseID: dax.DatabaseID(db),
SQL: sql,
}
if err := json.NewEncoder(&buf).Encode(sqlReq); err != nil {
return nil, errors.Wrapf(err, "encoding sql request: %s", sql)
func (qryr *serverlessQueryer) Query(org string, db string, sql io.Reader) (*featurebase.WireQueryResponse, error) {
if org == "" {
return nil, NewErrOrganizationRequired()
}
resp, err := http.Post(url, "application/json", &buf)
url := fmt.Sprintf("%s/queryer/databases/%s/sql", hostPort(qryr.Host, qryr.Port), db)
if db == "" {
url = fmt.Sprintf("%s/queryer/sql", hostPort(qryr.Host, qryr.Port))
}
client := &http.Client{
Timeout: time.Second * 30,
}
req, err := http.NewRequest(http.MethodPost, url, sql)
if err != nil {
return nil, errors.Wrapf(err, "posting query")
return nil, errors.Wrap(err, "creating new post request")
}
req.Header.Add("Content-Type", "text/plain")
req.Header.Add("OrganizationID", org)
var resp *http.Response
if resp, err = client.Do(req); err != nil {
return nil, errors.Wrap(err, "executing post request")
}
fullbod, err := io.ReadAll(resp.Body)

109
cli/replacer.go Normal file
View file

@ -0,0 +1,109 @@
package cli
import (
"strings"
"github.com/benhoyt/goawk/lexer"
)
// replacer can replace parts of a string based on some rules and the provided
// map[string]string. For example, the Command can replace strings with values
// in its `variables` map.
type replacer struct {
m map[string]string
}
func newReplacer(m map[string]string) *replacer {
return &replacer{
m: m,
}
}
// replace replaces all instances of the string pattern `:key` with the value at
// m[key]. For example we want something like this:
//
// GIVEN: `start :one,:'two', :"three" ::four ::`
//
// with map
//
// map[string]string{
// "one": "repl1",
// "three": "repl3",
// }
//
// WANT: `start repl1,:'two', "repl3" ::four ::`
func (r *replacer) replace(s string) string {
// If no variables have been added to the map, there's no need to parse the
// string for variable replacement.
if len(r.m) == 0 {
return s
}
line := []byte(s)
lex := lexer.NewLexer(line)
// finger contains the index into line at the start of non-variable text
// that we want to include, as-is in the output.
var finger int
// sb builds the string which will be the final output.
var sb strings.Builder
for {
pos, tok, _ := lex.Scan()
switch tok {
case lexer.COLON:
// last is the last normal character position before the colon.
last := pos.Column - 1
// Get the next byte to see if the colon value is quoted, and if so,
// whether its has single or double quotes.
b := lex.PeekByte()
// padding is the amount of padding we have to consider around the
// variable name. If the variable is not quoted, it doesn't require
// any padding. But if it has quotes, it needs 2 characters of
// paddings to accomodate the quotes.
padding := 0
// quote holds the character to use to quote the final, replaced
// output value. Because the lexer doesn't tell us how a certain
// `string` token was quoted, we need to keep track of that here so
// we can put them back.
quote := ""
switch b {
case byte('\''): // single quote
quote = `'`
padding = 2
case byte('"'): // double quote
quote = `"`
padding = 2
}
pos, tok, key := lex.Scan()
switch tok {
case lexer.NAME, lexer.STRING:
// Write the normal text up to the variable replacement
// position.
sb.Write(line[finger:last])
if v, ok := r.m[key]; ok {
// Write replaced variable with the quotes it had.
sb.WriteString(quote + v + quote)
} else {
// Since the variable was not found in the map, just write
// back what was already there.
sb.WriteString(":" + quote + key + quote)
}
// Reset finger to point to the next position after the
// variable.
finger = pos.Column + len(key) + padding - 1
}
case lexer.EOF:
// Write the remainder of the string and return.
sb.Write(line[finger:])
return sb.String()
}
}
}

109
cli/replacer_test.go Normal file
View file

@ -0,0 +1,109 @@
package cli
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func TestReplacer(t *testing.T) {
t.Run("general replace function", func(t *testing.T) {
m := map[string]string{
"v1": "newVone",
"v2": "newVtwo",
}
tests := []struct {
s string
m map[string]string
exp string
}{
{
// no variables present
s: "foo",
m: m,
exp: "foo",
},
{
// variable prefix, but not in map
s: ":foo",
m: m,
exp: ":foo",
},
{
// variable name match, but missing prefix
s: "v1",
m: m,
exp: "v1",
},
{
// variable name match
s: ":v1",
m: m,
exp: "newVone",
},
{
// two variables, the same, no space
s: ":v1:v1",
m: m,
exp: "newVonenewVone",
},
{
// two variables, different, no space
s: ":v1:v2",
m: m,
exp: "newVonenewVtwo",
},
{
// two variables, different, spaces
s: ":v1 :v2",
m: m,
exp: "newVone newVtwo",
},
{
// one variable, one non-variable, no space
s: ":v1:foo",
m: m,
exp: "newVone:foo",
},
{
// one non-variable, one variable, no space
s: "foo:v1",
m: m,
exp: "foonewVone",
},
{
// two variables, different, comma
s: ":v1, :v2",
m: m,
exp: "newVone, newVtwo",
},
{
// single quotes
s: ":'v1'",
m: m,
exp: "'newVone'",
},
{
// double quotes
s: `:"v2"`,
m: m,
exp: `"newVtwo"`,
},
{
// more quotes
s: `start :v1,:'two', :"v2" ::four :: `,
m: m,
exp: `start newVone,:'two', "newVtwo" ::four :: `,
},
}
for i, test := range tests {
t.Run(fmt.Sprintf("test-%d", i), func(t *testing.T) {
replacer := newReplacer(test.m)
assert.Equal(t, test.exp, replacer.replace(test.s))
})
}
})
}

133
cli/splitter.go Normal file
View file

@ -0,0 +1,133 @@
package cli
import (
"strings"
"github.com/pkg/errors"
)
// splitter is a line splitter which splits a line into queryParts and
// metaCommands. It may not be necessary to have this be a separate struct since
// it contains no members and just has the one `split()` method, but here we
// are.
type splitter struct {
replacer *replacer
}
func newSplitter(r *replacer) *splitter {
return &splitter{
replacer: r,
}
}
// split splits the given line into queryParts and metaCommands.
// If a metaCommand is found, everything after that is considered either arguments to that
// metaCommand, or additional metaCommands. In other words, queryParts can not follow
// metaCommands in the same line.
//
// A line can contain any of the following patterns:
// 1- [queryParts...]: "select * from tbl; select"
// 2- [metaCommands...]: "\! pwd \q"
// 3- [queryParts...][metaCommands...]: "select * from \i file.sql"
func (s *splitter) split(line string) ([]queryPart, []metaCommand, error) {
// Look for a comment line.
if strings.HasPrefix(line, "--") {
return nil, nil, nil
}
// Look for a meta command.
parts := strings.SplitN(line, `\`, 2)
switch len(parts) {
case 1:
// slice of queryParts (pattern 1)
if qps, err := s.splitQueryParts(strings.TrimSpace(parts[0])); err != nil {
return nil, nil, errors.Wrap(err, "splitting query parts")
} else {
return qps, nil, nil
}
case 2:
// slice of parts + slice of meta commands (pattern 3)
// or
// slice of meta commands (pattern 2)
qps, err := s.splitQueryParts(strings.TrimSpace(parts[0]))
if err != nil {
return nil, nil, errors.Wrap(err, "splitting query parts")
}
mcs, err := s.splitMetaCommands(strings.TrimSpace(parts[1]))
if err != nil {
return nil, nil, errors.Wrap(err, "splitting meta commands")
}
return qps, mcs, nil
}
return nil, nil, nil
}
func (s *splitter) splitQueryParts(line string) ([]queryPart, error) {
if line == "" {
return nil, nil
}
// Look for a termination character;
parts := strings.Split(line, terminationChar)
// Do variable replacement.
for i := range parts {
parts[i] = s.replacer.replace(parts[i])
}
if len(parts) == 1 {
part0 := strings.TrimSpace(parts[0])
return []queryPart{
newPartRaw(part0),
}, nil
}
qps := make([]queryPart, 0)
for i := range parts {
part := strings.TrimSpace(parts[i])
if part == "" {
// If the line starts with a ";", treat it as a terminator for a
// previous line.
if i == 0 {
qps = append(qps, &partTerminator{})
}
continue
}
qps = append(qps, newPartRaw(part))
if i < len(parts)-1 {
qps = append(qps, &partTerminator{})
}
}
return qps, nil
}
func (s *splitter) splitMetaCommands(in string) ([]metaCommand, error) {
parts := strings.Split(in, `\`)
if len(parts) == 1 {
mc, err := splitMetaCommand(parts[0], s.replacer)
if err != nil {
return nil, errors.Wrapf(err, "splitting meta command: %s", parts[0])
}
return []metaCommand{mc}, nil
}
mcs := make([]metaCommand, 0)
for i := range parts {
part := strings.TrimSpace(parts[i])
if part == "" {
continue
}
mc, err := splitMetaCommand(part, s.replacer)
if err != nil {
return nil, errors.Wrapf(err, "splitting meta command: %s", part)
}
mcs = append(mcs, mc)
}
return mcs, nil
}

146
cli/splitter_test.go Normal file
View file

@ -0,0 +1,146 @@
package cli
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func TestSplitter(t *testing.T) {
s := newSplitter(newReplacer(nil))
t.Run("Split", func(t *testing.T) {
tests := []struct {
line string
expQueryParts []queryPart
expMetaCommands []metaCommand
expError string
}{
{
line: `foo`,
expQueryParts: []queryPart{
newPartRaw("foo"),
},
},
{
line: `foo;`,
expQueryParts: []queryPart{
newPartRaw("foo"),
newPartTerminator(),
},
},
{
line: `foo; `,
expQueryParts: []queryPart{
newPartRaw("foo"),
newPartTerminator(),
},
},
{
line: `foo; ; `,
expQueryParts: []queryPart{
newPartRaw("foo"),
newPartTerminator(),
},
},
{
line: `foo; bar`,
expQueryParts: []queryPart{
newPartRaw("foo"),
newPartTerminator(),
newPartRaw("bar"),
},
},
{
line: `foo; bar;`,
expQueryParts: []queryPart{
newPartRaw("foo"),
newPartTerminator(),
newPartRaw("bar"),
newPartTerminator(),
},
},
{
line: `\q`,
expMetaCommands: []metaCommand{
&metaQuit{},
},
},
{
line: ` \p`,
expMetaCommands: []metaCommand{
&metaPrint{},
},
},
{
line: `\q \p`,
expMetaCommands: []metaCommand{
&metaQuit{},
&metaPrint{},
},
},
{
line: `\q \p arg1 arg2`,
expMetaCommands: []metaCommand{
&metaQuit{},
&metaPrint{},
},
},
{
line: `\set`,
expMetaCommands: []metaCommand{
&metaSet{
args: []string{},
},
},
},
{
line: `\set arg1 arg2`,
expMetaCommands: []metaCommand{
&metaSet{
args: []string{"arg1", "arg2"},
},
},
},
{
line: `\set 'arg1' 'arg2'`,
expMetaCommands: []metaCommand{
&metaSet{
args: []string{"arg1", "arg2"},
},
},
},
{
line: `\set 'arg1' '"arg2"'`,
expMetaCommands: []metaCommand{
&metaSet{
args: []string{"arg1", "\"arg2\""},
},
},
},
{
line: `\`,
expError: "unsupported meta-command:",
},
{
line: `\xyzxyz`,
expError: "unsupported meta-command:",
},
}
for i, tt := range tests {
t.Run(fmt.Sprintf("test-%d-%s", i, tt.line), func(t *testing.T) {
qps, mcs, err := s.split(tt.line)
if tt.expError != "" {
if assert.Error(t, err) {
assert.Contains(t, err.Error(), tt.expError)
}
return
}
assert.NoError(t, err)
assert.ElementsMatch(t, tt.expQueryParts, qps)
assert.ElementsMatch(t, tt.expMetaCommands, mcs)
})
}
})
}

53
cli/testdata/database vendored Normal file
View file

@ -0,0 +1,53 @@
// Show databases now that we have set org.
SEND:SHOW DATABASES;
EXPECT:+-----+------+-------+------------+------------+------------+-------+-------------+
EXPECT:| _id | name | owner | updated_by | created_at | updated_at | units | description |
EXPECT:+-----+------+-------+------------+------------+------------+-------+-------------+
EXPECT:+-----+------+-------+------------+------------+------------+-------+-------------+
EXPECT:
// Create db1.
SEND:CREATE DATABASE db1 WITH UNITS 1;
EXPECT:
// List databases via SHOW DATABASES.
SEND:SHOW DATABASES;
EXPECT:+--------------------------------------+------+-------+------------+----------------------+----------------------+-------+-------------+
EXPECT:| _id | name | owner | updated_by | created_at | updated_at | units | description |
EXPECT:+--------------------------------------+------+-------+------------+----------------------+----------------------+-------+-------------+
EXPECTCOMP:WithFormat:| {uuid} | db1 | | | {timestamp} | {timestamp} | 1 | |
EXPECT:+--------------------------------------+------+-------+------------+----------------------+----------------------+-------+-------------+
EXPECT:
// List databases via SHOW DATABASES.
SEND:\l
EXPECT:+--------------------------------------+------+-------+------------+----------------------+----------------------+-------+-------------+
EXPECT:| _id | name | owner | updated_by | created_at | updated_at | units | description |
EXPECT:+--------------------------------------+------+-------+------------+----------------------+----------------------+-------+-------------+
EXPECTCOMP:WithFormat:| {uuid} | db1 | | | {timestamp} | {timestamp} | 1 | |
EXPECT:+--------------------------------------+------+-------+------------+----------------------+----------------------+-------+-------------+
EXPECT:
// Check database connection.
SEND:\c
EXPECT:You are not connected to a database.
// Try connecting to an invalid database.
SEND:\c invalid
EXPECT:executing meta command: invalid database: invalid
// Try connecting with too many arguments.
SEND:\c db1 extra
EXPECT:executing meta command: meta command 'connect' takes zero or one argument
// Connect to a database.
SEND:\c db1
EXPECTCOMP:WithFormat:You are now connected to database "db1" ({uuid}).
// Disconnect from the current database.
SEND:\c -
EXPECT:You are not connected to a database.
// Connect to a database again.
SEND:\c db1
EXPECTCOMP:WithFormat:You are now connected to database "db1" ({uuid}).

10
cli/testdata/famous.csv vendored Normal file
View file

@ -0,0 +1,10 @@
"Id", "Name", "Short description", "Gender", "Country", "Occupation", "Birth year", "Death year", "Manner of death", "Age of death"
1, "George Washington", "1st president of the United States (17321799)", "Male", "United States of America; Kingdom of Great Britain", "Politician", "1732", "1799", "natural causes", "67"
2, "Douglas Adams", "English writer and humorist", "Male", "United Kingdom", "Artist", "1952", "2001", "natural causes", "49"
3, "Abraham Lincoln", "16th president of the United States (1809-1865)", "Male", "United States of America", "Politician", "1809", "1865", "homicide", "56"
4, "Wolfgang Amadeus Mozart", "Austrian composer of the Classical period", "Male", "Archduchy of Austria; Archbishopric of Salzburg", "Artist", "1756", "1791", "0", "35"
5, "Ludwig van Beethoven", "German classical and romantic composer", "Male", "Holy Roman Empire; Austrian Empire", "Artist", "1770", "1827", "0", "57"
6, "Jean-François Champollion", "French classical scholar", "Male", "Kingdom of France; First French Empire", "Egyptologist", "1790", "1832", "natural causes", "42"
7, "Paul Morand", "French writer", "Male", "France", "Artist", "1888", "1976", "0", "88"
8, "Claude Monet", "French impressionist painter (1840-1926)", "Male", "France", "Artist", "1840", "1926", "natural causes", "86"
1 Id Name Short description Gender Country Occupation Birth year Death year Manner of death Age of death
2 1 George Washington 1st president of the United States (1732–1799) Male United States of America; Kingdom of Great Britain Politician 1732 1799 natural causes 67
3 2 Douglas Adams English writer and humorist Male United Kingdom Artist 1952 2001 natural causes 49
4 3 Abraham Lincoln 16th president of the United States (1809-1865) Male United States of America Politician 1809 1865 homicide 56
5 4 Wolfgang Amadeus Mozart Austrian composer of the Classical period Male Archduchy of Austria; Archbishopric of Salzburg Artist 1756 1791 0 35
6 5 Ludwig van Beethoven German classical and romantic composer Male Holy Roman Empire; Austrian Empire Artist 1770 1827 0 57
7 6 Jean-François Champollion French classical scholar Male Kingdom of France; First French Empire Egyptologist 1790 1832 natural causes 42
8 7 Paul Morand French writer Male France Artist 1888 1976 0 88
9 8 Claude Monet French impressionist painter (1840-1926) Male France Artist 1840 1926 natural causes 86

8
cli/testdata/meta_bang vendored Normal file
View file

@ -0,0 +1,8 @@
SEND:\! echo 'foo'
EXPECT:foo
SEND:\! echo "foo"
EXPECT:"foo"
SEND:\!
EXPECT:executing meta command: meta command '!' requires at least one argument

17
cli/testdata/meta_cd vendored Normal file
View file

@ -0,0 +1,17 @@
// Make a directory so we can test \cd'ing into it.
SEND:\! mkdir cli-test-dir
SEND:\cd cli-test-dir
SEND:\cd ..
SEND:\! rmdir cli-test-dir
// TODO(tlt): before we do this, we should implement the ability to execute
// commands in a \set like:
// \set homedir `pwd`
// then we can store what directory we're in so we can move back to it
// at the end of the test
// Switch to home directory.
// SEND:\cd
// Expect error on extra argument to \cd.
SEND:\cd dir extra
EXPECT:executing meta command: meta command 'cd' takes zero or one argument

33
cli/testdata/meta_describe vendored Normal file
View file

@ -0,0 +1,33 @@
// TODO(tlt): we can't run this test until we get the system tables under control (i.e. sorted). Currently, fb_views is in a map with users, so the following can fail 50% of the time.
// Show tables for database by calling describe with no args.
// SEND:\d
// EXPECT:+-------------------------+-------------------------+-------+------------+----------------------+----------------------+-------+------------+------------------------+
// EXPECT:| _id | name | owner | updated_by | created_at | updated_at | keys | space_used | description |
// EXPECT:+-------------------------+-------------------------+-------+------------+----------------------+----------------------+-------+------------+------------------------+
// EXPECTCOMP:WithFormat:| fb_veiws | fb_views | | | {timestamp} | {timestamp} | true | 0 | system table for views |
// EXPECTCOMP:WithFormat:| users | users | | | {timestamp} | {timestamp} | false | 0 | |
// EXPECTCOMP:WithFormat:| fb_____________________ | fb_____________________ | | | {timestamp} | {timestamp} | false | 0 | |
// EXPECTCOMP:WithFormat:| fb_____________________ | fb_____________________ | | | {timestamp} | {timestamp} | false | 0 | |
// EXPECTCOMP:WithFormat:| fb_____________________ | fb_____________________ | | | {timestamp} | {timestamp} | false | 0 | |
// EXPECTCOMP:WithFormat:| fb_____________________ | fb_____________________ | | | {timestamp} | {timestamp} | false | 0 | |
// EXPECTCOMP:WithFormat:| fb_____________________ | fb_____________________ | | | {timestamp} | {timestamp} | false | 0 | |
// EXPECT:+-------------------------+-------------------------+-------+------------+----------------------+----------------------+-------+------------+------------------------+
// EXPECT:
// Show columns for table.
SEND:\d users
EXPECT:+------+------+--------+----------------------+-------+------------+------------+-------+----------------------+---------------------+----------+-------+-------------+-----+
EXPECT:| _id | name | type | created_at | keys | cache_type | cache_size | scale | min | max | timeunit | epoch | timequantum | ttl |
EXPECT:+------+------+--------+----------------------+-------+------------+------------+-------+----------------------+---------------------+----------+-------+-------------+-----+
EXPECTCOMP:WithFormat:| _id | _id | id | {timestamp} | false | | 0 | 0 | 0 | 0 | | 0 | | 0s |
EXPECTCOMP:WithFormat:| name | name | string | {timestamp} | true | ranked | 50000 | 0 | 0 | 0 | | 0 | | 0s |
EXPECTCOMP:WithFormat:| age | age | int | {timestamp} | false | | 0 | 0 | -9223372036854775808 | 9223372036854775807 | | 0 | | 0s |
EXPECT:+------+------+--------+----------------------+-------+------------+------------+-------+----------------------+---------------------+----------+-------+-------------+-----+
EXPECT:
// Show columns for an invalid table.
SEND:\d invalid
EXPECT:Error: compiling plan: [1:19] table 'invalid' not found
SEND:\d users extra
EXPECT:executing meta command: meta command 'describe' takes zero or one argument

6
cli/testdata/meta_echo vendored Normal file
View file

@ -0,0 +1,6 @@
SEND:\echo
EXPECT:
// Simple \echo.
SEND:\echo foo bar
EXPECT:foo bar

70
cli/testdata/meta_file vendored Normal file
View file

@ -0,0 +1,70 @@
// Create a table.
SEND:CREATE TABLE famous (
SEND: _id ID,
SEND: name STRING,
SEND: description STRING,
SEND: gender STRING,
SEND: country STRING,
SEND: occupation STRING,
SEND: birth_year INT min -32767 max 32767,
SEND: death_year INT min -32767 max 32767,
SEND: death_manner STRING,
SEND: birth_age INT min -32767 max 32767
SEND:);
EXPECT:
// Open bulk insert.
SEND:BULK INSERT
SEND:INTO famous (_id, name, description, gender, country, occupation,
SEND: birth_year, death_year, death_manner, birth_age )
SEND:MAP(0 INT,
SEND:1 STRING,
SEND:2 STRING,
SEND:3 STRING,
SEND:4 STRING,
SEND:5 STRING,
SEND:6 INT,
SEND:7 INT,
SEND:8 STRING,
SEND:9 INT )
SEND:FROM
SEND: x'
// Call \file
SEND:\file testdata/famous.csv
// Close bulk insert.
SEND:'
SEND:WITH
SEND: BATCHSIZE 100000
SEND: FORMAT 'CSV'
SEND: INPUT 'STREAM'
SEND: HEADER_ROW;
EXPECT:
// Query table to ensure we have data.
SEND:SELECT * FROM famous;
EXPECT:+-----+---------------------------+-------------------------------------------------+--------+----------------------------------------------------+--------------+------------+------------+----------------+-----------+
EXPECT:| _id | name | description | gender | country | occupation | birth_year | death_year | death_manner | birth_age |
EXPECT:+-----+---------------------------+-------------------------------------------------+--------+----------------------------------------------------+--------------+------------+------------+----------------+-----------+
EXPECT:| 1 | George Washington | 1st president of the United States (17321799) | Male | United States of America; Kingdom of Great Britain | Politician | 1732 | 1799 | natural causes | 67 |
EXPECT:| 2 | Douglas Adams | English writer and humorist | Male | United Kingdom | Artist | 1952 | 2001 | natural causes | 49 |
EXPECT:| 3 | Abraham Lincoln | 16th president of the United States (1809-1865) | Male | United States of America | Politician | 1809 | 1865 | homicide | 56 |
EXPECT:| 4 | Wolfgang Amadeus Mozart | Austrian composer of the Classical period | Male | Archduchy of Austria; Archbishopric of Salzburg | Artist | 1756 | 1791 | 0 | 35 |
EXPECT:| 5 | Ludwig van Beethoven | German classical and romantic composer | Male | Holy Roman Empire; Austrian Empire | Artist | 1770 | 1827 | 0 | 57 |
EXPECT:| 6 | Jean-François Champollion | French classical scholar | Male | Kingdom of France; First French Empire | Egyptologist | 1790 | 1832 | natural causes | 42 |
EXPECT:| 7 | Paul Morand | French writer | Male | France | Artist | 1888 | 1976 | 0 | 88 |
EXPECT:| 8 | Claude Monet | French impressionist painter (1840-1926) | Male | France | Artist | 1840 | 1926 | natural causes | 86 |
EXPECT:+-----+---------------------------+-------------------------------------------------+--------+----------------------------------------------------+--------------+------------+------------+----------------+-----------+
EXPECT:
// TODO(tlt): dropping the table seems to cause problems.
// Drop the table.
//SEND:DROP TABLE famous;
// Ensure that invalid aruments (none or too many) return an error.
SEND:\file
EXPECT:executing meta command: meta command 'file' requires exactly one argument
SEND:\file filename extra
EXPECT:executing meta command: meta command 'file' requires exactly one argument

24
cli/testdata/meta_include vendored Normal file
View file

@ -0,0 +1,24 @@
// Include with no argument should error.
SEND:\i
EXPECT:executing meta command: meta command 'include' requires exactly one argument
// Include with too many arguments should error.
SEND:\include testdata/people.sql extra
EXPECT:executing meta command: meta command 'include' requires exactly one argument
// Invalid file should error.
SEND:\include invalid.file
EXPECT:executing meta command: opening file: invalid.file: open invalid.file: no such file or directory
SEND:\include testdata/people.sql
EXPECT:
EXPECT:
EXPECT:+-----+------+-----+
EXPECT:| _id | name | age |
EXPECT:+-----+------+-----+
EXPECT:| 1 | Amy | 42 |
EXPECT:| 2 | Bob | 27 |
EXPECT:| 3 | Carl | 33 |
EXPECT:+-----+------+-----+
EXPECT:
EXPECT:mix in a meta command

67
cli/testdata/meta_output vendored Normal file
View file

@ -0,0 +1,67 @@
SEND:SELECT * FROM users;
EXPECT:+-----+-------+-----+
EXPECT:| _id | name | age |
EXPECT:+-----+-------+-----+
EXPECT:| 1 | Anne | 38 |
EXPECT:| 2 | Bill | 23 |
EXPECT:| 3 | Cindy | 64 |
EXPECT:+-----+-------+-----+
EXPECT:
// Redirect output to a file.
SEND:\o test-output-file
SEND:SELECT * FROM users;
// Ensure the output went to the file.
SEND:\! cat test-output-file
EXPECT:+-----+-------+-----+
EXPECT:| _id | name | age |
EXPECT:+-----+-------+-----+
EXPECT:| 1 | Anne | 38 |
EXPECT:| 2 | Bill | 23 |
EXPECT:| 3 | Cindy | 64 |
EXPECT:+-----+-------+-----+
EXPECT:
// Let's test some qecho stuff here while we're at it.
SEND:\qecho string with "double quotes"
SEND:\qecho -n one
SEND:\qecho -n two
SEND:\qecho three
SEND:\qecho four
SEND:\! cat test-output-file
EXPECT:+-----+-------+-----+
EXPECT:| _id | name | age |
EXPECT:+-----+-------+-----+
EXPECT:| 1 | Anne | 38 |
EXPECT:| 2 | Bill | 23 |
EXPECT:| 3 | Cindy | 64 |
EXPECT:+-----+-------+-----+
EXPECT:
EXPECT:string with "double quotes"
EXPECT:onetwothree
EXPECT:four
// And \warn messages should still go to stderr, not the file.
SEND:\warn a warning string
EXPECT:a warning string
// Remove the file.
SEND:\! rm test-output-file
// Set the output back to stdout.
SEND:\o
SEND:SELECT * FROM users;
EXPECT:+-----+-------+-----+
EXPECT:| _id | name | age |
EXPECT:+-----+-------+-----+
EXPECT:| 1 | Anne | 38 |
EXPECT:| 2 | Bill | 23 |
EXPECT:| 3 | Cindy | 64 |
EXPECT:+-----+-------+-----+
EXPECT:
// Ensure extra arguments to \output causes an error.
SEND:\o filename extra
EXPECT:executing meta command: meta command 'output' takes zero or one argument

52
cli/testdata/meta_pset_border vendored Normal file
View file

@ -0,0 +1,52 @@
SEND:\pset border 2
EXPECT:Border style is 2.
SEND:SELECT 1 as foo, 'baz' as bar;
EXPECT:+-----+-----+
EXPECT:| foo | bar |
EXPECT:+-----+-----+
EXPECT:| 1 | baz |
EXPECT:+-----+-----+
EXPECT:
SEND:\pset border
EXPECT:Border style is 2.
SEND:\pset border 999
EXPECT:Border style is 0.
SEND:\pset border 1
EXPECT:Border style is 1.
SEND:SELECT 1 as foo, 'baz' as bar;
EXPECT: foo | bar
EXPECT:-----+-----
EXPECT: 1 | baz
EXPECT:
SEND:\pset border 2
EXPECT:Border style is 2.
SEND:SELECT 1 as foo, 'baz' as bar;
EXPECT:+-----+-----+
EXPECT:| foo | bar |
EXPECT:+-----+-----+
EXPECT:| 1 | baz |
EXPECT:+-----+-----+
EXPECT:
SEND:\pset border 0
EXPECT:Border style is 0.
SEND:SELECT 1 as foo, 'baz' as bar;
EXPECT:foo bar
EXPECT:--- ---
EXPECT: 1 baz
EXPECT:
SEND:\pset border 1 extra
EXPECT:executing meta command: meta command 'pset' takes zero, one, or two arguments
// Set border back to the testing default.
SEND:\pset border 2
EXPECT:Border style is 2.

35
cli/testdata/meta_pset_expanded vendored Normal file
View file

@ -0,0 +1,35 @@
// Set to off.
SEND:\pset expanded off
EXPECT:Expanded display is off.
// Set to on.
SEND:\pset expanded on
EXPECT:Expanded display is on.
// Toggle to off.
SEND:\pset expanded
EXPECT:Expanded display is off.
// Toggle to on.
SEND:\pset expanded
EXPECT:Expanded display is on.
// Set to something invalid.
SEND:\pset expanded invalid
EXPECT:executing meta command: unrecognized value "invalid" for "expanded": Boolean expected
// Ensure expanded shows results vertically.
SEND:SELECT 1 as foo, 'baz' as bar;
EXPECT:+-----+-----+
EXPECT:| foo | 1 |
EXPECT:| bar | baz |
EXPECT:+-----+-----+
EXPECT:
// Set back to off as we started.
SEND:\pset expanded off
EXPECT:Expanded display is off.
// make sure the \x meta-command returns expected errors
SEND:\x on extra
EXPECT:executing meta command: meta command 'expanded' takes zero or one argument

47
cli/testdata/meta_pset_format_csv vendored Normal file
View file

@ -0,0 +1,47 @@
SEND:\pset format csv
EXPECT:Output format is csv.
SEND:SELECT * FROM users;
EXPECT:_id,name,age
EXPECT:1,Anne,38
EXPECT:2,Bill,23
EXPECT:3,Cindy,64
// Exclude headers.
SEND:\t on
EXPECT:Tuples only is on.
SEND:SELECT * FROM users;
EXPECT:1,Anne,38
EXPECT:2,Bill,23
EXPECT:3,Cindy,64
// Reset headers.
SEND:\t off
EXPECT:Tuples only is off.
// Set expanded to on.
SEND:\x on
EXPECT:Expanded display is on.
SEND:SELECT * FROM users;
EXPECT:_id,1
EXPECT:name,Anne
EXPECT:age,38
EXPECT:_id,2
EXPECT:name,Bill
EXPECT:age,23
EXPECT:_id,3
EXPECT:name,Cindy
EXPECT:age,64
// Set expanded back to off.
SEND:\x off
EXPECT:Expanded display is off.
// Set format back to aligned as we started.
SEND:\pset format aligned
EXPECT:Output format is aligned.
SEND:\pset format invalid
EXPECT:executing meta command: \pset: allowed formats are aligned, csv

34
cli/testdata/meta_pset_tuples_only vendored Normal file
View file

@ -0,0 +1,34 @@
// Set to off.
SEND:\pset tuples_only off
EXPECT:Tuples only is off.
// Set to on.
SEND:\pset tuples_only on
EXPECT:Tuples only is on.
// Toggle to off.
SEND:\pset tuples_only
EXPECT:Tuples only is off.
// Toggle to on.
SEND:\pset tuples_only
EXPECT:Tuples only is on.
// Set to something invalid.
SEND:\pset tuples_only invalid
EXPECT:executing meta command: unrecognized value "invalid" for "tuples_only": Boolean expected
// Ensure tuples_only shows only tuples.
SEND:SELECT 1 as foo, 'baz' as bar;
EXPECT:+---+-----+
EXPECT:| 1 | baz |
EXPECT:+---+-----+
EXPECT:
// Set back to off as we started.
SEND:\pset tuples_only off
EXPECT:Tuples only is off.
// make sure the \t meta-command returns expected errors
SEND:\t off extra
EXPECT:executing meta command: meta command 'tuples_only' takes zero or one argument

31
cli/testdata/meta_set vendored Normal file
View file

@ -0,0 +1,31 @@
SEND:\set
SEND:\set var1 foo
SEND:\set
EXPECT:var1 = 'foo'
SEND:\set var2 bar
SEND:\set
EXPECT:var1 = 'foo'
EXPECT:var2 = 'bar'
SEND:\set var3 zoo
SEND:\set
EXPECT:var1 = 'foo'
EXPECT:var2 = 'bar'
EXPECT:var3 = 'zoo'
SEND:\unset
EXPECT:\unset: missing required argument
SEND:\unset non-existent-key
SEND:\unset var1
SEND:\set
EXPECT:var2 = 'bar'
EXPECT:var3 = 'zoo'
SEND:\unset var2 extra
EXPECT:\unset: extra argument "extra" ignored
SEND:\set
EXPECT:var3 = 'zoo'

50
cli/testdata/meta_timing vendored Normal file
View file

@ -0,0 +1,50 @@
// Start by ensuring timing is off.
SEND:\timing off
EXPECT:Timing is off.
// Set timing on.
SEND:\timing on
EXPECT:Timing is on.
// Toggle timing.
SEND:\timing
EXPECT:Timing is off.
// Toggle timing again.
SEND:\timing
EXPECT:Timing is on.
// Send extra argument to \timing.
SEND:\timing on extra
EXPECT:executing meta command: meta command 'timing' takes zero or one argument
SEND:SELECT * FROM users;
EXPECT:+-----+-------+-----+
EXPECT:| _id | name | age |
EXPECT:+-----+-------+-----+
EXPECT:| 1 | Anne | 38 |
EXPECT:| 2 | Bill | 23 |
EXPECT:| 3 | Cindy | 64 |
EXPECT:+-----+-------+-----+
EXPECT:
EXPECTCOMP:HasPrefix:Execution time:
// Turn timing back off.
SEND:\timing off
EXPECT:Timing is off.
// Ensure we don't get timing.
SEND:SELECT * FROM users;
EXPECT:+-----+-------+-----+
EXPECT:| _id | name | age |
EXPECT:+-----+-------+-----+
EXPECT:| 1 | Anne | 38 |
EXPECT:| 2 | Bill | 23 |
EXPECT:| 3 | Cindy | 64 |
EXPECT:+-----+-------+-----+
EXPECT:
// Ensure an invalid timing value returns an error.
SEND:\timing invalid
EXPECT:executing meta command: unrecognized value "invalid" for "\timing": Boolean expected

24
cli/testdata/meta_write vendored Normal file
View file

@ -0,0 +1,24 @@
// Make sure there's something in the query buffer.
// This is left unterminated because we don't need to execute the query;
// we just need there to be something in the buffer.
SEND:SELECT * FROM invalid-table
SEND:\write query-buffer-contents
// Reset the buffer.
SEND:\r
EXPECT:Query buffer reset (cleared).
// Read from the file.
SEND:\! cat query-buffer-contents
EXPECT:SELECT * FROM invalid-table
// Remove the file.
SEND:\! rm query-buffer-contents
// Send \write with no arguments.
SEND:\write
EXPECT:\w: missing required argument
// Send \write with extra arguments.
SEND:\write filename extra
EXPECT:executing meta command: meta command 'w' exactly one argument

12
cli/testdata/people.sql vendored Normal file
View file

@ -0,0 +1,12 @@
-- Create a table.
create table people (_id id, name string, age int);
-- Insert some values.
insert into people values (1, 'Amy', 42), (2, 'Bob', 27), (3, 'Carl', 33);
-- Get all rows from the table.
select * from people;
-- Mix in a meta-command to show that both are supported
-- in the include file.
\echo mix in a meta command

40
cli/testdata/query_buffer vendored Normal file
View file

@ -0,0 +1,40 @@
SEND:select 1 as foo;
EXPECT:+-----+
EXPECT:| foo |
EXPECT:+-----+
EXPECT:| 1 |
EXPECT:+-----+
EXPECT:
SEND:\p
EXPECT:select 1 as foo;
SEND:select 2
SEND:\p
EXPECT:select 2
SEND:\r
EXPECT:Query buffer reset (cleared).
SEND:\p
EXPECT:select 1 as foo;
SEND:select 3
SEND:\p
EXPECT:select 3
SEND:as foo
SEND:\p
EXPECT:select 3
EXPECT:as foo
SEND:;
EXPECT:+-----+
EXPECT:| foo |
EXPECT:+-----+
EXPECT:| 3 |
EXPECT:+-----+
EXPECT:
SEND:\p
EXPECT:select 3
EXPECT:as foo;

51
cli/testdata/setup vendored Normal file
View file

@ -0,0 +1,51 @@
// Startup splash.
EXPECT:FeatureBase CLI ()
EXPECT:Type "\q" to quit.
EXPECT:Detected on-prem, serverless deployment.
EXPECTCOMP:HasPrefix:Host: http://localhost:
EXPECT:You are not connected to a database.
// Show databases.
SEND:SHOW DATABASES;
EXPECT:Organization required. Use \org to set an organization.
// Get current org.
SEND:\org
EXPECT:You have not set an organization.
// Set org.
SEND:\org acme
EXPECT:You have set organization "acme".
// Try to set org with too many arguments.
SEND:\org acme extra
EXPECT:executing meta command: meta command 'org' takes zero or one argument
// Set location to UTC so that expected timestamp size is consistent.
// Without this, a test running locally in may have a timestamp that
// ends in a timezone offset such as `-06:00`, while one running as UTC
// will have `Z`. Since these string lengths differ, our generic
// {timestamp} comparison will fail.
SEND:\pset location UTC
EXPECT:Location is UTC.
// Set an invalid location.
SEND:\pset location invalid
EXPECT:executing meta command: loading location: invalid: unknown time zone invalid
// Try to set location with too many arguments.
SEND:\pset location UTC extra
EXPECT:executing meta command: meta command 'pset' takes zero, one, or two arguments
// Set border to 2 for testing because it makes it easier to visually see
// what the tests are expecting (because lines don't end in spaces).
SEND:\pset border 2
EXPECT:Border style is 2.
// Check the state of pset.
SEND:\pset
EXPECT:border 2
EXPECT:expanded off
EXPECT:format aligned
EXPECT:location UTC
EXPECT:tuples_only off

72
cli/testdata/table vendored Normal file
View file

@ -0,0 +1,72 @@
// Show tables for database using SHOW TABLES WITH SYSTEM.
SEND:SHOW TABLES WITH SYSTEM;
EXPECT:+-------------------------+-------------------------+-------+------------+----------------------+----------------------+-------+------------+-------------+
EXPECT:| _id | name | owner | updated_by | created_at | updated_at | keys | space_used | description |
EXPECT:+-------------------------+-------------------------+-------+------------+----------------------+----------------------+-------+------------+-------------+
EXPECTCOMP:WithFormat:| fb_____________________ | fb_____________________ | | | {timestamp} | {timestamp} | false | 0 | |
EXPECTCOMP:WithFormat:| fb_____________________ | fb_____________________ | | | {timestamp} | {timestamp} | false | 0 | |
EXPECTCOMP:WithFormat:| fb_____________________ | fb_____________________ | | | {timestamp} | {timestamp} | false | 0 | |
EXPECTCOMP:WithFormat:| fb_____________________ | fb_____________________ | | | {timestamp} | {timestamp} | false | 0 | |
EXPECTCOMP:WithFormat:| fb_____________________ | fb_____________________ | | | {timestamp} | {timestamp} | false | 0 | |
EXPECT:+-------------------------+-------------------------+-------+------------+----------------------+----------------------+-------+------------+-------------+
EXPECT:
// Show tables for database using \d.
SEND:\d
EXPECT:+-------------------------+-------------------------+-------+------------+----------------------+----------------------+-------+------------+-------------+
EXPECT:| _id | name | owner | updated_by | created_at | updated_at | keys | space_used | description |
EXPECT:+-------------------------+-------------------------+-------+------------+----------------------+----------------------+-------+------------+-------------+
EXPECTCOMP:WithFormat:| fb_____________________ | fb_____________________ | | | {timestamp} | {timestamp} | false | 0 | |
EXPECTCOMP:WithFormat:| fb_____________________ | fb_____________________ | | | {timestamp} | {timestamp} | false | 0 | |
EXPECTCOMP:WithFormat:| fb_____________________ | fb_____________________ | | | {timestamp} | {timestamp} | false | 0 | |
EXPECTCOMP:WithFormat:| fb_____________________ | fb_____________________ | | | {timestamp} | {timestamp} | false | 0 | |
EXPECTCOMP:WithFormat:| fb_____________________ | fb_____________________ | | | {timestamp} | {timestamp} | false | 0 | |
EXPECT:+-------------------------+-------------------------+-------+------------+----------------------+----------------------+-------+------------+-------------+
EXPECT:
// Show tables for database using SHOW TABLES.
SEND:SHOW TABLES;
EXPECT:+-----+------+-------+------------+------------+------------+------+------------+-------------+
EXPECT:| _id | name | owner | updated_by | created_at | updated_at | keys | space_used | description |
EXPECT:+-----+------+-------+------------+------------+------------+------+------------+-------------+
EXPECT:+-----+------+-------+------------+------------+------------+------+------------+-------------+
EXPECT:
// Show tables for database using \dt.
SEND:\dt
EXPECT:+-----+------+-------+------------+------------+------------+------+------------+-------------+
EXPECT:| _id | name | owner | updated_by | created_at | updated_at | keys | space_used | description |
EXPECT:+-----+------+-------+------------+------------+------------+------+------------+-------------+
EXPECT:+-----+------+-------+------------+------------+------------+------+------------+-------------+
EXPECT:
// Create a table. That can be used for general testing.
SEND:CREATE TABLE users (_id id, name string, age int);
EXPECT:
SEND:INSERT INTO users VALUES (1, 'Anne', 38), (2, 'Bill', 23), (3, 'Cindy', 64);
EXPECT:
// Show tables for database to get the newly created table.
SEND:\dt
EXPECT:+-------+-------+-------+------------+----------------------+----------------------+-------+------------+-------------+
EXPECT:| _id | name | owner | updated_by | created_at | updated_at | keys | space_used | description |
EXPECT:+-------+-------+-------+------------+----------------------+----------------------+-------+------------+-------------+
EXPECTCOMP:WithFormat:| users | users | | | {timestamp} | {timestamp} | false | 0 | |
EXPECT:+-------+-------+-------+------------+----------------------+----------------------+-------+------------+-------------+
EXPECT:
// We don't select from users until AFTER we check SHOW TABLES above because
// running this creates the fb_views sytem table which has a description.
// And it's annoying to mask out all of the description fields because we
// don't know which row fb_views will fall into.
SEND:SELECT * FROM users;
EXPECT:+-----+-------+-----+
EXPECT:| _id | name | age |
EXPECT:+-----+-------+-----+
EXPECT:| 1 | Anne | 38 |
EXPECT:| 2 | Bill | 23 |
EXPECT:| 3 | Cindy | 64 |
EXPECT:+-----+-------+-----+
EXPECT:

21
cli/workingdir.go Normal file
View file

@ -0,0 +1,21 @@
package cli
import (
"os"
)
// workingDir was originally set up with the intention of using it to maintain a
// reference to the current working directory. But it turns out we haven't
// really needed that so far. The `cd()` method is unsed in one of the meta
// commands, but we could probably just call `os.Chdir()` directly there. With
// that said, I'm leaving it here for now until we're abosolutely sure we don't
// need to use this for other directory/file handling functionality.
type workingDir struct{}
func newWorkingDir() *workingDir {
return &workingDir{}
}
func (wd *workingDir) cd(dir string) error {
return os.Chdir(dir)
}

294
cli/writer.go Normal file
View file

@ -0,0 +1,294 @@
package cli
import (
"encoding/csv"
"fmt"
"io"
"log"
"time"
featurebase "github.com/featurebasedb/featurebase/v3"
"github.com/jedib0t/go-pretty/table"
"github.com/jedib0t/go-pretty/text"
"github.com/pkg/errors"
)
// writeOptions contains user configuration options which describe how to write
// the query output.
type writeOptions struct {
border int
expanded bool
format string
location *time.Location
timing bool
tuplesOnly bool
}
const (
formatAligned = "aligned"
formatCSV = "csv"
)
func defaultWriteOptions() *writeOptions {
return &writeOptions{
border: 1,
expanded: false,
format: formatAligned,
location: time.Local,
timing: false,
tuplesOnly: false,
}
}
// writeOutput writes the query response, taking the format into consideration.
// It sends query output to qOut, non-error informational output (such as query
// timing) to wOut, and errors to wErr.
func writeOutput(r *featurebase.WireQueryResponse, opts *writeOptions, qOut io.Writer, wOut io.Writer, wErr io.Writer) error {
if r == nil {
return errors.New("attempt to write out nil response")
}
if r.Error != "" {
if _, err := wErr.Write([]byte("Error: " + r.Error + "\n")); err != nil {
return errors.Wrapf(err, "writing error: %s", r.Error)
}
return writeWarnings(r, wErr)
}
switch opts.format {
case formatAligned:
if err := writeTable(r, opts, qOut); err != nil {
return errors.Wrap(err, "writing table")
}
// Add some white space after query results.
qOut.Write([]byte("\n"))
case formatCSV:
if err := writeCSV(r, opts, qOut); err != nil {
return errors.Wrap(err, "writing csv")
}
default:
return errors.Errorf("invalid format: %s", opts.format)
}
if err := writeWarnings(r, wErr); err != nil {
return err
}
// Timing.
if opts.timing {
if _, err := wOut.Write([]byte(fmt.Sprintf("Execution time: %dμs\n", r.ExecutionTime))); err != nil {
return errors.Wrapf(err, "writing execution time: %s", r.Error)
}
}
return nil
}
// writeCSV writes the WireQueryResponse to qOut as csv.
func writeCSV(r *featurebase.WireQueryResponse, opts *writeOptions, qOut io.Writer) error {
w := csv.NewWriter(qOut)
if opts.expanded {
// Expanded csv
// rec is used to write the row as a slice of strings. It is reused to
// avoid unnecessary memory allocation.
rec := make([]string, 2)
for _, row := range r.Data {
cleanRow(row, opts)
for i, col := range r.Schema.Fields {
rec[0] = string(col.Name)
rec[1] = fmt.Sprintf("%v", row[i])
// Write the record.
if err := w.Write(rec); err != nil {
log.Fatalln("error writing expanded record to csv:", err)
}
}
}
} else {
// Normal csv (i.e. NOT expanded)
// Write the schema.
if !opts.tuplesOnly {
header := make([]string, 0, len(r.Schema.Fields))
for i := range r.Schema.Fields {
header = append(header, string(r.Schema.Fields[i].Name))
}
if err := w.Write(header); err != nil {
return errors.Wrapf(err, "error writing header to csv")
}
}
// Write the records.
// rec is used to write the row as a slice of strings. It is reused to
// avoid unnecessary memory allocation.
rec := make([]string, len(r.Schema.Fields))
for _, row := range r.Data {
cleanRow(row, opts)
for i := range row {
rec[i] = fmt.Sprintf("%v", row[i])
}
if err := w.Write(rec); err != nil {
log.Fatalln("error writing record to csv:", err)
}
}
}
// Write any buffered data to the underlying writer (standard output).
w.Flush()
return w.Error()
}
// writeTable writes the WireQueryResponse to qOut in a tabular format.
func writeTable(r *featurebase.WireQueryResponse, opts *writeOptions, qOut io.Writer) error {
t := table.NewWriter()
t.SetOutputMirror(qOut)
switch opts.border {
case 0:
t.SetStyle(styleBorder0)
case 1:
t.SetStyle(styleBorder1)
default:
t.SetStyle(styleBorder2)
// In expanded mode with a border, we need borders between each record.
if opts.expanded {
t.Style().Options.SeparateRows = true
}
}
// Don't uppercase the header values.
t.Style().Format.Header = text.FormatDefault
if opts.expanded {
// Expanded table
for _, row := range r.Data {
cleanRow(row, opts)
colRow := make([]interface{}, 2)
scolRow := make([]string, 2)
div := "\n"
for i, col := range r.Schema.Fields {
if i == len(r.Schema.Fields)-1 {
div = ""
}
scolRow[0] += fmt.Sprintf("%s%s", col.Name, div)
scolRow[1] += fmt.Sprintf("%v%s", row[i], div)
}
colRow[0] = scolRow[0]
colRow[1] = scolRow[1]
t.AppendRow(table.Row(colRow[:]))
}
} else {
// Normal table (i.e. NOT expanded)
if !opts.tuplesOnly {
t.AppendHeader(schemaToRow(r.Schema))
}
for _, row := range r.Data {
cleanRow(row, opts)
t.AppendRow(table.Row(row))
}
}
t.Render()
return nil
}
// cleanRow loops through all the columns of row and modifies its value based on
// type.
//
// If the value is nil, replace it with a null string; go-pretty doesn't expect
// nil pointers in the data values.
//
// If the value is a time.Time, we want to print it using RFC3339Nano to be
// consistent with everything else.
func cleanRow(row []interface{}, opts *writeOptions) {
for i := range row {
switch v := row[i].(type) {
case nil:
row[i] = nullValue
case time.Time:
row[i] = v.In(opts.location).Format(time.RFC3339Nano)
}
}
}
func schemaToRow(schema featurebase.WireQuerySchema) []interface{} {
ret := make([]interface{}, len(schema.Fields))
for i, field := range schema.Fields {
ret[i] = field.Name
}
return ret
}
func writeWarnings(r *featurebase.WireQueryResponse, w io.Writer) error {
if len(r.Warnings) == 0 {
return nil
}
if _, err := w.Write([]byte("\n")); err != nil {
return errors.Wrapf(err, "writing line feed")
}
for _, warning := range r.Warnings {
if _, err := w.Write([]byte("Warning: " + warning + "\n")); err != nil {
return errors.Wrapf(err, "writing warning: %s", warning)
}
}
return nil
}
var styleBorder2 table.Style = table.StyleDefault
var styleBorder1 table.Style = table.Style{
Name: "StyleBorder1",
Box: table.StyleBoxDefault,
Color: table.ColorOptionsDefault,
Format: table.FormatOptionsDefault,
Options: table.Options{
DrawBorder: false,
SeparateColumns: true,
SeparateFooter: true,
SeparateHeader: true,
SeparateRows: false,
},
Title: table.TitleOptionsDefault,
}
var styleBorder0 table.Style = table.Style{
Name: "StyleBorder0",
Box: table.BoxStyle{
BottomLeft: "+",
BottomRight: "+",
BottomSeparator: "+",
Left: "|",
LeftSeparator: "+",
MiddleHorizontal: "-",
MiddleSeparator: " ",
MiddleVertical: " ",
PaddingLeft: "",
PaddingRight: "",
PageSeparator: "\n",
Right: "|",
RightSeparator: "+",
TopLeft: "+",
TopRight: "+",
TopSeparator: "+",
UnfinishedRow: " ~",
},
Color: table.ColorOptionsDefault,
Format: table.FormatOptionsDefault,
Options: table.Options{
DrawBorder: false,
SeparateColumns: true,
SeparateFooter: true,
SeparateHeader: true,
SeparateRows: false,
},
Title: table.TitleOptionsDefault,
}

188
cli/writer_test.go Normal file
View file

@ -0,0 +1,188 @@
package cli
import (
"bytes"
"fmt"
"strings"
"testing"
featurebase "github.com/featurebasedb/featurebase/v3"
dax "github.com/featurebasedb/featurebase/v3/dax"
"github.com/stretchr/testify/assert"
)
func TestWriter(t *testing.T) {
t.Run("writeTable", func(t *testing.T) {
wqr := &featurebase.WireQueryResponse{
Schema: featurebase.WireQuerySchema{
Fields: []*featurebase.WireQueryField{
{Name: "_id", Type: dax.BaseTypeID},
{Name: "name", Type: dax.BaseTypeString},
{Name: "age", Type: dax.BaseTypeInt},
},
},
Data: [][]interface{}{
{1, "Amy", 44},
{2, "Bob", 32},
{3, "Cindy", 28},
},
}
// TODO(tlt): used for debugging
// format := defaultWriteOptions()
// assert.NoError(t, writeTable(wqr, format, os.Stdout, os.Stdout, os.Stdout))
// return
tests := []struct {
format *writeOptions
expQOut string
expOut string
expErr string
}{
{
// default format
format: defaultWriteOptions(),
expQOut: stringOfLines(
" _id | name | age ",
"-----+-------+-----",
" 1 | Amy | 44 ",
" 2 | Bob | 32 ",
" 3 | Cindy | 28 ",
"",
),
expOut: "",
expErr: "",
},
{
// timing on
format: &writeOptions{
border: 1,
expanded: false,
format: formatAligned,
timing: true,
tuplesOnly: false,
},
expQOut: stringOfLines(
" _id | name | age ",
"-----+-------+-----",
" 1 | Amy | 44 ",
" 2 | Bob | 32 ",
" 3 | Cindy | 28 ",
"",
),
expOut: "Execution time: 0μs\n",
expErr: "",
},
{
// format.border = 2 (or higher)
format: &writeOptions{
border: 2,
expanded: false,
format: formatAligned,
timing: false,
tuplesOnly: false,
},
expQOut: stringOfLines(
"+-----+-------+-----+",
"| _id | name | age |",
"+-----+-------+-----+",
"| 1 | Amy | 44 |",
"| 2 | Bob | 32 |",
"| 3 | Cindy | 28 |",
"+-----+-------+-----+",
"",
),
expOut: "",
expErr: "",
},
{
// format.border = 0
format: &writeOptions{
border: 0,
expanded: false,
format: formatAligned,
timing: false,
tuplesOnly: false,
},
expQOut: stringOfLines(
"_id name age",
"--- ----- ---",
" 1 Amy 44",
" 2 Bob 32",
" 3 Cindy 28",
"",
),
expOut: "",
expErr: "",
},
{
// format.tuplesOnly = true
format: &writeOptions{
border: 1,
expanded: false,
format: formatAligned,
timing: false,
tuplesOnly: true,
},
expQOut: stringOfLines(
" 1 | Amy | 44 ",
" 2 | Bob | 32 ",
" 3 | Cindy | 28 ",
"",
),
expOut: "",
expErr: "",
},
{
// format.border = 2, expanded
format: &writeOptions{
border: 2,
expanded: true,
format: formatAligned,
timing: false,
tuplesOnly: false,
},
expQOut: stringOfLines(
"+------+-------+",
"| _id | 1 |",
"| name | Amy |",
"| age | 44 |",
"+------+-------+",
"| _id | 2 |",
"| name | Bob |",
"| age | 32 |",
"+------+-------+",
"| _id | 3 |",
"| name | Cindy |",
"| age | 28 |",
"+------+-------+",
"",
),
expOut: "",
expErr: "",
},
}
for i, test := range tests {
t.Run(fmt.Sprintf("test-%d", i), func(t *testing.T) {
// Set up buffers to capture the output.
qOut := bytes.NewBuffer(make([]byte, 0, 100000))
wOut := bytes.NewBuffer(make([]byte, 0, 100000))
wErr := bytes.NewBuffer(make([]byte, 0, 100000))
assert.NoError(t, writeOutput(wqr, test.format, qOut, wOut, wErr))
assert.Equal(t, test.expQOut, qOut.String())
assert.Equal(t, test.expOut, wOut.String())
assert.Equal(t, test.expErr, wErr.String())
})
}
})
}
func stringOfLines(lines ...string) string {
var sb strings.Builder
for _, line := range lines {
sb.WriteString(line + "\n")
}
return sb.String()
}

View file

@ -3,10 +3,10 @@ package client
import (
"context"
featurebase "github.com/molecula/featurebase/v3"
"github.com/molecula/featurebase/v3/client/types"
"github.com/molecula/featurebase/v3/dax"
"github.com/molecula/featurebase/v3/errors"
featurebase "github.com/featurebasedb/featurebase/v3"
"github.com/featurebasedb/featurebase/v3/client/types"
"github.com/featurebasedb/featurebase/v3/dax"
"github.com/featurebasedb/featurebase/v3/errors"
)
var _ featurebase.SchemaAPI = &schemaAPI{}
@ -26,6 +26,26 @@ func NewSchemaAPI(c *Client) *schemaAPI {
}
}
func (s *schemaAPI) CreateDatabase(context.Context, *dax.Database) error {
return errors.Errorf("unimplemented: schemaAPI.CreateDatabase()")
}
func (s *schemaAPI) DropDatabase(context.Context, dax.DatabaseID) error {
return errors.Errorf("unimplemented: schemaAPI.DropDatabase()")
}
func (s *schemaAPI) DatabaseByName(ctx context.Context, dbname dax.DatabaseName) (*dax.Database, error) {
return nil, errors.Errorf("unimplemented: schemaAPI.DatabaseByName()")
}
func (s *schemaAPI) DatabaseByID(ctx context.Context, dbid dax.DatabaseID) (*dax.Database, error) {
return nil, errors.Errorf("unimplemented: schemaAPI.DatabaseByID()")
}
func (s *schemaAPI) SetDatabaseOption(ctx context.Context, dbid dax.DatabaseID, option string, value string) error {
return nil
}
func (s *schemaAPI) Databases(context.Context, ...dax.DatabaseID) ([]*dax.Database, error) {
return nil, errors.Errorf("unimplemented: schemaAPI.Databases()")
}
func (s *schemaAPI) TableByName(ctx context.Context, tname dax.TableName) (*dax.Table, error) {
return nil, errors.New(errors.ErrUncoded, "schemaAPI.TableByName not implemented")
}

View file

@ -20,16 +20,16 @@ import (
"sync"
"time"
pilosa "github.com/featurebasedb/featurebase/v3"
"github.com/featurebasedb/featurebase/v3/client/types"
fbproto "github.com/featurebasedb/featurebase/v3/encoding/proto" // TODO use this everywhere and get rid of proto import
"github.com/featurebasedb/featurebase/v3/logger"
pnet "github.com/featurebasedb/featurebase/v3/net"
"github.com/featurebasedb/featurebase/v3/pb"
"github.com/featurebasedb/featurebase/v3/pql"
"github.com/featurebasedb/featurebase/v3/roaring"
"github.com/featurebasedb/featurebase/v3/vprint"
"github.com/golang/protobuf/proto" //nolint:staticcheck
pilosa "github.com/molecula/featurebase/v3"
"github.com/molecula/featurebase/v3/client/types"
fbproto "github.com/molecula/featurebase/v3/encoding/proto" // TODO use this everywhere and get rid of proto import
"github.com/molecula/featurebase/v3/logger"
pnet "github.com/molecula/featurebase/v3/net"
"github.com/molecula/featurebase/v3/pb"
"github.com/molecula/featurebase/v3/pql"
"github.com/molecula/featurebase/v3/roaring"
"github.com/molecula/featurebase/v3/vprint"
"github.com/opentracing/opentracing-go"
"github.com/pkg/errors"
"golang.org/x/sync/errgroup"

View file

@ -1,4 +1,5 @@
// Copyright 2021 Molecula Corp. All rights reserved.
// Copyright 2022 Molecula Corp. (DBA FeatureBase).
// SPDX-License-Identifier: Apache-2.0
package client
import (
@ -8,13 +9,13 @@ import (
"testing"
"time"
featurebase "github.com/molecula/featurebase/v3"
client_types "github.com/molecula/featurebase/v3/client/types"
"github.com/molecula/featurebase/v3/disco"
pnet "github.com/molecula/featurebase/v3/net"
"github.com/molecula/featurebase/v3/roaring"
"github.com/molecula/featurebase/v3/shardwidth"
"github.com/molecula/featurebase/v3/test"
featurebase "github.com/featurebasedb/featurebase/v3"
client_types "github.com/featurebasedb/featurebase/v3/client/types"
"github.com/featurebasedb/featurebase/v3/disco"
pnet "github.com/featurebasedb/featurebase/v3/net"
"github.com/featurebasedb/featurebase/v3/roaring"
"github.com/featurebasedb/featurebase/v3/shardwidth"
"github.com/featurebasedb/featurebase/v3/test"
"github.com/stretchr/testify/require"
"golang.org/x/sync/errgroup"
)
@ -227,7 +228,7 @@ func TestClientAgainstCluster(t *testing.T) {
_, err = cli.Query(qry)
require.NoErrorf(t, err, "BatchQuery")
// XXX: The following is required to make this test pass. See: https://github.com/molecula/featurebase/issues/625
// XXX: The following is required to make this test pass. See: https://github.com/featurebasedb/featurebase/issues/625
_, _, err = cli.HTTPRequest("POST", "/recalculate-caches", nil, nil)
require.NoErrorf(t, err, "POST /recalculate-caches")

View file

@ -1,4 +1,5 @@
// Copyright 2021 Molecula Corp. All rights reserved.
// Copyright 2022 Molecula Corp. (DBA FeatureBase).
// SPDX-License-Identifier: Apache-2.0
// package ctl contains all pilosa subcommands other than 'server'. These are
// generally administration, testing, and debugging tools.
@ -10,7 +11,7 @@ import (
"reflect"
"testing"
pnet "github.com/molecula/featurebase/v3/net"
pnet "github.com/featurebasedb/featurebase/v3/net"
)
func TestQueryWithError(t *testing.T) {

View file

@ -1,4 +1,5 @@
// Copyright 2021 Molecula Corp. All rights reserved.
// Copyright 2022 Molecula Corp. (DBA FeatureBase).
// SPDX-License-Identifier: Apache-2.0
// package ctl contains all pilosa subcommands other than 'server'. These are
// generally administration, testing, and debugging tools.
@ -7,7 +8,7 @@ package client
import (
"sync"
pnet "github.com/molecula/featurebase/v3/net"
pnet "github.com/featurebasedb/featurebase/v3/net"
)
// Cluster contains hosts in a Pilosa cluster.

View file

@ -1,4 +1,5 @@
// Copyright 2021 Molecula Corp. All rights reserved.
// Copyright 2022 Molecula Corp. (DBA FeatureBase).
// SPDX-License-Identifier: Apache-2.0
// package ctl contains all pilosa subcommands other than 'server'. These are
// generally administration, testing, and debugging tools.
@ -7,7 +8,7 @@ package client
import (
"testing"
pnet "github.com/molecula/featurebase/v3/net"
pnet "github.com/featurebasedb/featurebase/v3/net"
)
func TestNewClusterWithHost(t *testing.T) {

View file

@ -1,4 +1,5 @@
// Copyright 2021 Molecula Corp. All rights reserved.
// Copyright 2022 Molecula Corp. (DBA FeatureBase).
// SPDX-License-Identifier: Apache-2.0
package csv
import (
@ -10,7 +11,7 @@ import (
"strings"
"time"
"github.com/molecula/featurebase/v3/client"
"github.com/featurebasedb/featurebase/v3/client"
)
// Format is the format of the data in the CSV file.

Some files were not shown because too many files have changed in this diff Show more