From 690dd55a866ede9d2f376fe7643c46cf3d822309 Mon Sep 17 00:00:00 2001 From: Rushmore Mushambi Date: Sat, 3 Feb 2024 00:10:47 +0200 Subject: [PATCH] Extract core lib into a standalone crate (#3423) Co-authored-by: Gerard Guillemas Martos --- .github/workflows/ci.yml | 2 + Cargo.lock | 88 +- Cargo.toml | 11 +- Makefile.ci.toml | 33 +- Makefile.local.toml | 14 +- cackle.toml | 14 +- core/Cargo.toml | 187 ++ {lib => core}/src/cf/gc.rs | 0 {lib => core}/src/cf/mod.rs | 0 {lib => core}/src/cf/mutations.rs | 0 {lib => core}/src/cf/reader.rs | 0 {lib => core}/src/cf/writer.rs | 0 {lib => core}/src/cnf/mod.rs | 2 +- {lib => core}/src/ctx/cancellation.rs | 0 {lib => core}/src/ctx/canceller.rs | 0 {lib => core}/src/ctx/context.rs | 0 {lib => core}/src/ctx/mod.rs | 0 {lib => core}/src/ctx/reason.rs | 0 {lib => core}/src/dbs/capabilities.rs | 61 - {lib => core}/src/dbs/distinct.rs | 0 {lib => core}/src/dbs/executor.rs | 0 {lib => core}/src/dbs/explanation.rs | 0 {lib => core}/src/dbs/iterator.rs | 0 {lib => core}/src/dbs/mod.rs | 0 {lib => core}/src/dbs/node.rs | 0 {lib => core}/src/dbs/notification.rs | 0 {lib => core}/src/dbs/options.rs | 0 {lib => core}/src/dbs/processor.rs | 0 {lib => core}/src/dbs/response.rs | 3 +- {lib => core}/src/dbs/session.rs | 0 {lib => core}/src/dbs/statement.rs | 0 {lib => core}/src/dbs/test.rs | 0 {lib => core}/src/dbs/transaction.rs | 0 {lib => core}/src/dbs/variables.rs | 0 {lib => core}/src/doc/allow.rs | 0 {lib => core}/src/doc/alter.rs | 0 {lib => core}/src/doc/changefeeds.rs | 0 {lib => core}/src/doc/check.rs | 0 {lib => core}/src/doc/clean.rs | 0 {lib => core}/src/doc/compute.rs | 0 {lib => core}/src/doc/create.rs | 0 {lib => core}/src/doc/delete.rs | 0 {lib => core}/src/doc/document.rs | 0 {lib => core}/src/doc/edges.rs | 0 {lib => core}/src/doc/empty.rs | 0 {lib => core}/src/doc/erase.rs | 0 {lib => core}/src/doc/event.rs | 0 {lib => core}/src/doc/field.rs | 0 {lib => core}/src/doc/index.rs | 0 {lib => core}/src/doc/insert.rs | 0 {lib => core}/src/doc/lives.rs | 0 {lib => core}/src/doc/merge.rs | 0 {lib => core}/src/doc/mod.rs | 0 {lib => core}/src/doc/pluck.rs | 0 {lib => core}/src/doc/process.rs | 0 {lib => core}/src/doc/purge.rs | 0 {lib => core}/src/doc/relate.rs | 0 {lib => core}/src/doc/reset.rs | 0 {lib => core}/src/doc/select.rs | 0 {lib => core}/src/doc/store.rs | 0 {lib => core}/src/doc/table.rs | 0 {lib => core}/src/doc/update.rs | 0 {lib => core}/src/env/mod.rs | 0 {lib => core}/src/err/mod.rs | 0 {lib => core}/src/exe/mod.rs | 0 {lib => core}/src/exe/spawn.rs | 0 .../src/exe/try_join_all_buffered.rs | 0 {lib => core}/src/fflags.rs | 0 {lib => core}/src/fnc/args.rs | 0 {lib => core}/src/fnc/array.rs | 0 {lib => core}/src/fnc/bytes.rs | 0 {lib => core}/src/fnc/count.rs | 0 {lib => core}/src/fnc/crypto.rs | 0 {lib => core}/src/fnc/duration.rs | 0 {lib => core}/src/fnc/encoding.rs | 0 {lib => core}/src/fnc/geo.rs | 0 {lib => core}/src/fnc/http.rs | 0 {lib => core}/src/fnc/math.rs | 0 {lib => core}/src/fnc/meta.rs | 0 {lib => core}/src/fnc/mod.rs | 0 {lib => core}/src/fnc/not.rs | 0 {lib => core}/src/fnc/object.rs | 0 {lib => core}/src/fnc/operate.rs | 0 {lib => core}/src/fnc/parse.rs | 0 {lib => core}/src/fnc/rand.rs | 0 .../src/fnc/script/classes/duration.rs | 0 {lib => core}/src/fnc/script/classes/mod.rs | 0 .../src/fnc/script/classes/record.rs | 0 {lib => core}/src/fnc/script/classes/uuid.rs | 0 {lib => core}/src/fnc/script/error.rs | 0 {lib => core}/src/fnc/script/fetch/body.rs | 0 .../src/fnc/script/fetch/classes/blob.rs | 0 .../src/fnc/script/fetch/classes/form_data.rs | 0 .../src/fnc/script/fetch/classes/headers.rs | 0 .../src/fnc/script/fetch/classes/mod.rs | 0 .../src/fnc/script/fetch/classes/request.rs | 0 .../fnc/script/fetch/classes/response/init.rs | 0 .../fnc/script/fetch/classes/response/mod.rs | 0 {lib => core}/src/fnc/script/fetch/func.rs | 0 {lib => core}/src/fnc/script/fetch/mod.rs | 0 {lib => core}/src/fnc/script/fetch/stream.rs | 0 {lib => core}/src/fnc/script/fetch/util.rs | 0 .../src/fnc/script/fetch_stub/mod.rs | 0 .../src/fnc/script/fetch_stub/test.rs | 0 {lib => core}/src/fnc/script/from.rs | 0 .../src/fnc/script/globals/console.rs | 0 {lib => core}/src/fnc/script/globals/mod.rs | 0 {lib => core}/src/fnc/script/into.rs | 0 {lib => core}/src/fnc/script/main.rs | 0 {lib => core}/src/fnc/script/mod.rs | 0 {lib => core}/src/fnc/script/modules/mod.rs | 0 {lib => core}/src/fnc/script/modules/os.rs | 0 .../modules/surrealdb/functions/array.rs | 0 .../modules/surrealdb/functions/array/sort.rs | 0 .../modules/surrealdb/functions/bytes.rs | 0 .../modules/surrealdb/functions/crypto.rs | 0 .../surrealdb/functions/crypto/argon2.rs | 0 .../surrealdb/functions/crypto/bcrypt.rs | 0 .../surrealdb/functions/crypto/pbkdf2.rs | 0 .../surrealdb/functions/crypto/scrypt.rs | 0 .../modules/surrealdb/functions/duration.rs | 0 .../surrealdb/functions/duration/from.rs | 0 .../modules/surrealdb/functions/encoding.rs | 0 .../surrealdb/functions/encoding/base64.rs | 0 .../script/modules/surrealdb/functions/geo.rs | 0 .../modules/surrealdb/functions/geo/hash.rs | 0 .../modules/surrealdb/functions/http.rs | 0 .../modules/surrealdb/functions/math.rs | 0 .../modules/surrealdb/functions/meta.rs | 0 .../script/modules/surrealdb/functions/mod.rs | 0 .../modules/surrealdb/functions/object.rs | 0 .../modules/surrealdb/functions/parse.rs | 0 .../surrealdb/functions/parse/email.rs | 0 .../modules/surrealdb/functions/parse/url.rs | 0 .../modules/surrealdb/functions/rand.rs | 0 .../modules/surrealdb/functions/rand/uuid.rs | 0 .../modules/surrealdb/functions/search.rs | 0 .../modules/surrealdb/functions/session.rs | 0 .../modules/surrealdb/functions/string.rs | 0 .../surrealdb/functions/string/distance.rs | 0 .../modules/surrealdb/functions/string/is.rs | 0 .../surrealdb/functions/string/semver.rs | 0 .../surrealdb/functions/string/semver/inc.rs | 0 .../surrealdb/functions/string/semver/set.rs | 0 .../surrealdb/functions/string/similarity.rs | 0 .../modules/surrealdb/functions/time.rs | 0 .../modules/surrealdb/functions/time/from.rs | 0 .../modules/surrealdb/functions/type.rs | 0 .../modules/surrealdb/functions/type/is.rs | 0 .../modules/surrealdb/functions/vector.rs | 0 .../surrealdb/functions/vector/distance.rs | 0 .../surrealdb/functions/vector/similarity.rs | 0 .../src/fnc/script/modules/surrealdb/mod.rs | 0 .../script/modules/surrealdb/query/classes.rs | 0 .../fnc/script/modules/surrealdb/query/mod.rs | 0 {lib => core}/src/fnc/script/tests/fetch.rs | 0 {lib => core}/src/fnc/script/tests/mod.rs | 0 {lib => core}/src/fnc/search.rs | 0 {lib => core}/src/fnc/session.rs | 0 {lib => core}/src/fnc/sleep.rs | 0 {lib => core}/src/fnc/string.rs | 0 {lib => core}/src/fnc/time.rs | 0 {lib => core}/src/fnc/type.rs | 0 {lib => core}/src/fnc/util/geo/mod.rs | 0 {lib => core}/src/fnc/util/http/mod.rs | 0 {lib => core}/src/fnc/util/math/bottom.rs | 0 {lib => core}/src/fnc/util/math/deviation.rs | 0 .../src/fnc/util/math/interquartile.rs | 0 {lib => core}/src/fnc/util/math/mean.rs | 0 {lib => core}/src/fnc/util/math/median.rs | 0 {lib => core}/src/fnc/util/math/midhinge.rs | 0 {lib => core}/src/fnc/util/math/mod.rs | 0 {lib => core}/src/fnc/util/math/mode.rs | 0 .../src/fnc/util/math/nearestrank.rs | 0 {lib => core}/src/fnc/util/math/percentile.rs | 0 {lib => core}/src/fnc/util/math/quartile.rs | 0 {lib => core}/src/fnc/util/math/spread.rs | 0 {lib => core}/src/fnc/util/math/top.rs | 0 {lib => core}/src/fnc/util/math/trimean.rs | 0 {lib => core}/src/fnc/util/math/variance.rs | 0 {lib => core}/src/fnc/util/math/vector.rs | 0 {lib => core}/src/fnc/util/mod.rs | 0 {lib => core}/src/fnc/util/string/fuzzy.rs | 0 {lib => core}/src/fnc/util/string/mod.rs | 0 {lib => core}/src/fnc/util/string/slug.rs | 0 {lib => core}/src/fnc/vector.rs | 0 {lib => core}/src/iam/auth.rs | 0 {lib => core}/src/iam/base.rs | 0 {lib => core}/src/iam/check.rs | 0 {lib => core}/src/iam/clear.rs | 0 {lib => core}/src/iam/entities/action.rs | 0 {lib => core}/src/iam/entities/mod.rs | 0 .../src/iam/entities/resources/actor.rs | 0 .../src/iam/entities/resources/level.rs | 0 .../src/iam/entities/resources/mod.rs | 0 .../src/iam/entities/resources/resource.rs | 0 {lib => core}/src/iam/entities/roles.rs | 0 {lib => core}/src/iam/entities/schema.rs | 0 {lib => core}/src/iam/jwks.rs | 6 +- {lib => core}/src/iam/mod.rs | 0 {lib => core}/src/iam/policies/mod.rs | 0 {lib => core}/src/iam/policies/policy_set.rs | 0 {lib => core}/src/iam/signin.rs | 0 {lib => core}/src/iam/signup.rs | 0 {lib => core}/src/iam/token.rs | 0 {lib => core}/src/iam/verify.rs | 2 +- {lib => core}/src/idg/mod.rs | 0 {lib => core}/src/idg/u32.rs | 0 {lib => core}/src/idx/docids.rs | 0 {lib => core}/src/idx/ft/analyzer/filter.rs | 0 {lib => core}/src/idx/ft/analyzer/mod.rs | 0 .../src/idx/ft/analyzer/tokenizer.rs | 0 {lib => core}/src/idx/ft/doclength.rs | 0 {lib => core}/src/idx/ft/highlighter.rs | 0 {lib => core}/src/idx/ft/mod.rs | 0 {lib => core}/src/idx/ft/offsets.rs | 0 {lib => core}/src/idx/ft/postings.rs | 0 {lib => core}/src/idx/ft/scorer.rs | 0 {lib => core}/src/idx/ft/termdocs.rs | 0 {lib => core}/src/idx/ft/terms.rs | 0 {lib => core}/src/idx/mod.rs | 0 {lib => core}/src/idx/planner/executor.rs | 0 {lib => core}/src/idx/planner/iterators.rs | 0 {lib => core}/src/idx/planner/knn.rs | 0 {lib => core}/src/idx/planner/mod.rs | 0 {lib => core}/src/idx/planner/plan.rs | 0 {lib => core}/src/idx/planner/tree.rs | 0 {lib => core}/src/idx/trees/bkeys.rs | 0 {lib => core}/src/idx/trees/btree.rs | 0 {lib => core}/src/idx/trees/mod.rs | 0 {lib => core}/src/idx/trees/mtree.rs | 0 {lib => core}/src/idx/trees/store/cache.rs | 0 {lib => core}/src/idx/trees/store/mod.rs | 0 {lib => core}/src/idx/trees/store/tree.rs | 0 {lib => core}/src/idx/trees/vector.rs | 0 {lib => core}/src/key/change/mod.rs | 0 {lib => core}/src/key/database/all.rs | 0 {lib => core}/src/key/database/az.rs | 0 {lib => core}/src/key/database/fc.rs | 0 {lib => core}/src/key/database/ml.rs | 0 {lib => core}/src/key/database/mod.rs | 0 {lib => core}/src/key/database/pa.rs | 0 {lib => core}/src/key/database/sc.rs | 0 {lib => core}/src/key/database/tb.rs | 0 {lib => core}/src/key/database/ti.rs | 0 {lib => core}/src/key/database/tk.rs | 0 {lib => core}/src/key/database/ts.rs | 0 {lib => core}/src/key/database/us.rs | 0 {lib => core}/src/key/database/vs.rs | 0 {lib => core}/src/key/debug.rs | 0 {lib => core}/src/key/error.rs | 0 {lib => core}/src/key/graph/mod.rs | 0 {lib => core}/src/key/index/all.rs | 0 {lib => core}/src/key/index/bc.rs | 0 {lib => core}/src/key/index/bd.rs | 0 {lib => core}/src/key/index/bf.rs | 0 {lib => core}/src/key/index/bi.rs | 0 {lib => core}/src/key/index/bk.rs | 0 {lib => core}/src/key/index/bl.rs | 0 {lib => core}/src/key/index/bo.rs | 0 {lib => core}/src/key/index/bp.rs | 0 {lib => core}/src/key/index/bs.rs | 0 {lib => core}/src/key/index/bt.rs | 0 {lib => core}/src/key/index/bu.rs | 0 {lib => core}/src/key/index/mod.rs | 0 {lib => core}/src/key/index/vm.rs | 0 {lib => core}/src/key/key_req.rs | 0 {lib => core}/src/key/mod.rs | 0 {lib => core}/src/key/namespace/all.rs | 0 {lib => core}/src/key/namespace/db.rs | 0 {lib => core}/src/key/namespace/di.rs | 0 {lib => core}/src/key/namespace/mod.rs | 0 {lib => core}/src/key/namespace/tk.rs | 0 {lib => core}/src/key/namespace/us.rs | 0 {lib => core}/src/key/node/all.rs | 0 {lib => core}/src/key/node/lq.rs | 0 {lib => core}/src/key/node/mod.rs | 0 {lib => core}/src/key/root/all.rs | 0 {lib => core}/src/key/root/hb.rs | 0 {lib => core}/src/key/root/mod.rs | 0 {lib => core}/src/key/root/nd.rs | 0 {lib => core}/src/key/root/ni.rs | 0 {lib => core}/src/key/root/ns.rs | 0 {lib => core}/src/key/root/us.rs | 0 {lib => core}/src/key/scope/all.rs | 0 {lib => core}/src/key/scope/mod.rs | 0 {lib => core}/src/key/scope/tk.rs | 0 {lib => core}/src/key/table/all.rs | 0 {lib => core}/src/key/table/ev.rs | 0 {lib => core}/src/key/table/fd.rs | 0 {lib => core}/src/key/table/ft.rs | 0 {lib => core}/src/key/table/ix.rs | 0 {lib => core}/src/key/table/lq.rs | 0 {lib => core}/src/key/table/mod.rs | 0 {lib => core}/src/key/thing/mod.rs | 0 {lib => core}/src/kvs/cache.rs | 0 {lib => core}/src/kvs/clock.rs | 0 {lib => core}/src/kvs/ds.rs | 69 +- {lib => core}/src/kvs/fdb/mod.rs | 0 {lib => core}/src/kvs/indxdb/mod.rs | 0 {lib => core}/src/kvs/kv.rs | 0 {lib => core}/src/kvs/mem/mod.rs | 0 {lib => core}/src/kvs/mod.rs | 0 {lib => core}/src/kvs/rocksdb/cnf.rs | 0 {lib => core}/src/kvs/rocksdb/mod.rs | 0 {lib => core}/src/kvs/speedb/cnf.rs | 0 {lib => core}/src/kvs/speedb/mod.rs | 0 {lib => core}/src/kvs/tests/cluster_init.rs | 0 {lib => core}/src/kvs/tests/hb.rs | 0 {lib => core}/src/kvs/tests/helper.rs | 0 {lib => core}/src/kvs/tests/lq.rs | 0 {lib => core}/src/kvs/tests/mod.rs | 0 {lib => core}/src/kvs/tests/multireader.rs | 0 .../kvs/tests/multiwriter_different_keys.rs | 0 .../kvs/tests/multiwriter_same_keys_allow.rs | 0 .../tests/multiwriter_same_keys_conflict.rs | 0 {lib => core}/src/kvs/tests/nd.rs | 0 {lib => core}/src/kvs/tests/ndlq.rs | 0 {lib => core}/src/kvs/tests/nq.rs | 0 {lib => core}/src/kvs/tests/raw.rs | 0 {lib => core}/src/kvs/tests/snapshot.rs | 0 {lib => core}/src/kvs/tests/tb.rs | 0 {lib => core}/src/kvs/tests/tblq.rs | 0 {lib => core}/src/kvs/tests/tbnt.rs | 0 .../kvs/tests/timestamp_to_versionstamp.rs | 0 {lib => core}/src/kvs/tikv/mod.rs | 0 {lib => core}/src/kvs/tx.rs | 0 core/src/lib.rs | 53 + {lib => core}/src/mac/mod.rs | 6 + {lib => core}/src/obs/mod.rs | 0 core/src/sql/mod.rs | 9 + {lib/src/sql => core/src/sql/v1}/algorithm.rs | 0 {lib/src/sql => core/src/sql/v1}/arbitrary.rs | 0 {lib/src/sql => core/src/sql/v1}/array.rs | 0 {lib/src/sql => core/src/sql/v1}/base.rs | 0 {lib/src/sql => core/src/sql/v1}/block.rs | 0 {lib/src/sql => core/src/sql/v1}/bytes.rs | 0 {lib/src/sql => core/src/sql/v1}/cast.rs | 0 .../src/sql => core/src/sql/v1}/changefeed.rs | 0 {lib/src/sql => core/src/sql/v1}/cond.rs | 0 {lib/src/sql => core/src/sql/v1}/constant.rs | 0 {lib/src/sql => core/src/sql/v1}/data.rs | 0 {lib/src/sql => core/src/sql/v1}/datetime.rs | 0 {lib/src/sql => core/src/sql/v1}/dir.rs | 0 {lib/src/sql => core/src/sql/v1}/duration.rs | 0 {lib/src/sql => core/src/sql/v1}/edges.rs | 0 {lib/src/sql => core/src/sql/v1}/ending.rs | 0 {lib/src/sql => core/src/sql/v1}/escape.rs | 0 {lib/src/sql => core/src/sql/v1}/explain.rs | 0 .../src/sql => core/src/sql/v1}/expression.rs | 3 +- {lib/src/sql => core/src/sql/v1}/fetch.rs | 0 {lib/src/sql => core/src/sql/v1}/field.rs | 0 {lib/src/sql => core/src/sql/v1}/filter.rs | 0 {lib/src/sql => core/src/sql/v1}/fmt.rs | 0 {lib/src/sql => core/src/sql/v1}/function.rs | 0 {lib/src/sql => core/src/sql/v1}/future.rs | 0 {lib/src/sql => core/src/sql/v1}/geometry.rs | 0 {lib/src/sql => core/src/sql/v1}/graph.rs | 0 {lib/src/sql => core/src/sql/v1}/group.rs | 0 {lib/src/sql => core/src/sql/v1}/id.rs | 0 {lib/src/sql => core/src/sql/v1}/ident.rs | 0 {lib/src/sql => core/src/sql/v1}/idiom.rs | 0 {lib/src/sql => core/src/sql/v1}/index.rs | 0 {lib/src/sql => core/src/sql/v1}/kind.rs | 0 {lib/src/sql => core/src/sql/v1}/language.rs | 0 {lib/src/sql => core/src/sql/v1}/limit.rs | 0 {lib/src/sql => core/src/sql/v1}/mock.rs | 0 {lib/src/sql => core/src/sql/v1}/mod.rs | 4 +- {lib/src/sql => core/src/sql/v1}/model.rs | 0 {lib/src/sql => core/src/sql/v1}/number.rs | 0 {lib/src/sql => core/src/sql/v1}/object.rs | 0 {lib/src/sql => core/src/sql/v1}/operation.rs | 0 {lib/src/sql => core/src/sql/v1}/operator.rs | 0 {lib/src/sql => core/src/sql/v1}/order.rs | 0 {lib/src/sql => core/src/sql/v1}/output.rs | 0 {lib/src/sql => core/src/sql/v1}/param.rs | 0 {lib/src/sql => core/src/sql/v1}/part.rs | 0 {lib/src/sql => core/src/sql/v1}/paths.rs | 0 .../src/sql => core/src/sql/v1}/permission.rs | 0 {lib/src/sql => core/src/sql/v1}/query.rs | 0 {lib/src/sql => core/src/sql/v1}/range.rs | 0 {lib/src/sql => core/src/sql/v1}/regex.rs | 0 {lib/src/sql => core/src/sql/v1}/scoring.rs | 0 {lib/src/sql => core/src/sql/v1}/script.rs | 0 {lib/src/sql => core/src/sql/v1}/serde.rs | 0 {lib/src/sql => core/src/sql/v1}/split.rs | 0 {lib/src/sql => core/src/sql/v1}/start.rs | 0 {lib/src/sql => core/src/sql/v1}/statement.rs | 0 .../src/sql/v1}/statements/analyze.rs | 0 .../src/sql/v1}/statements/begin.rs | 0 .../src/sql/v1}/statements/break.rs | 0 .../src/sql/v1}/statements/cancel.rs | 0 .../src/sql/v1}/statements/commit.rs | 0 .../src/sql/v1}/statements/continue.rs | 0 .../src/sql/v1}/statements/create.rs | 0 .../src/sql/v1}/statements/define/analyzer.rs | 0 .../src/sql/v1}/statements/define/database.rs | 0 .../src/sql/v1}/statements/define/event.rs | 0 .../src/sql/v1}/statements/define/field.rs | 0 .../src/sql/v1}/statements/define/function.rs | 0 .../src/sql/v1}/statements/define/index.rs | 0 .../src/sql/v1}/statements/define/mod.rs | 0 .../src/sql/v1}/statements/define/model.rs | 0 .../sql/v1}/statements/define/namespace.rs | 0 .../src/sql/v1}/statements/define/param.rs | 0 .../src/sql/v1}/statements/define/scope.rs | 0 .../src/sql/v1}/statements/define/table.rs | 0 .../src/sql/v1}/statements/define/token.rs | 0 .../src/sql/v1}/statements/define/user.rs | 0 .../src/sql/v1}/statements/delete.rs | 0 .../src/sql/v1}/statements/foreach.rs | 0 .../src/sql/v1}/statements/ifelse.rs | 0 .../src/sql/v1}/statements/info.rs | 0 .../src/sql/v1}/statements/insert.rs | 0 .../src/sql/v1}/statements/kill.rs | 0 .../src/sql/v1}/statements/live.rs | 10 + .../sql => core/src/sql/v1}/statements/mod.rs | 0 .../src/sql/v1}/statements/option.rs | 0 .../src/sql/v1}/statements/output.rs | 0 .../src/sql/v1}/statements/relate.rs | 0 .../src/sql/v1}/statements/remove/analyzer.rs | 0 .../src/sql/v1}/statements/remove/database.rs | 0 .../src/sql/v1}/statements/remove/event.rs | 0 .../src/sql/v1}/statements/remove/field.rs | 0 .../src/sql/v1}/statements/remove/function.rs | 0 .../src/sql/v1}/statements/remove/index.rs | 0 .../src/sql/v1}/statements/remove/mod.rs | 0 .../src/sql/v1}/statements/remove/model.rs | 0 .../sql/v1}/statements/remove/namespace.rs | 0 .../src/sql/v1}/statements/remove/param.rs | 0 .../src/sql/v1}/statements/remove/scope.rs | 0 .../src/sql/v1}/statements/remove/table.rs | 0 .../src/sql/v1}/statements/remove/token.rs | 0 .../src/sql/v1}/statements/remove/user.rs | 0 .../src/sql/v1}/statements/select.rs | 0 .../sql => core/src/sql/v1}/statements/set.rs | 0 .../src/sql/v1}/statements/show.rs | 0 .../src/sql/v1}/statements/sleep.rs | 0 .../src/sql/v1}/statements/throw.rs | 0 .../src/sql/v1}/statements/update.rs | 0 .../sql => core/src/sql/v1}/statements/use.rs | 0 {lib/src/sql => core/src/sql/v1}/strand.rs | 0 {lib/src/sql => core/src/sql/v1}/subquery.rs | 0 {lib/src/sql => core/src/sql/v1}/table.rs | 0 {lib/src/sql => core/src/sql/v1}/thing.rs | 0 {lib/src/sql => core/src/sql/v1}/timeout.rs | 0 {lib/src/sql => core/src/sql/v1}/tokenizer.rs | 0 {lib/src/sql => core/src/sql/v1}/uuid.rs | 0 {lib/src/sql => core/src/sql/v1}/value/all.rs | 0 .../sql => core/src/sql/v1}/value/changed.rs | 0 .../sql => core/src/sql/v1}/value/clear.rs | 0 .../sql => core/src/sql/v1}/value/compare.rs | 0 {lib/src/sql => core/src/sql/v1}/value/cut.rs | 0 {lib/src/sql => core/src/sql/v1}/value/dec.rs | 0 .../src/sql/v1}/value/decrement.rs | 0 {lib/src/sql => core/src/sql/v1}/value/def.rs | 0 {lib/src/sql => core/src/sql/v1}/value/del.rs | 0 .../src/sql => core/src/sql/v1}/value/diff.rs | 0 .../src/sql => core/src/sql/v1}/value/each.rs | 0 .../sql => core/src/sql/v1}/value/every.rs | 0 .../sql => core/src/sql/v1}/value/extend.rs | 0 .../sql => core/src/sql/v1}/value/fetch.rs | 0 .../sql => core/src/sql/v1}/value/first.rs | 0 .../sql => core/src/sql/v1}/value/flatten.rs | 0 .../sql => core/src/sql/v1}/value/generate.rs | 0 {lib/src/sql => core/src/sql/v1}/value/get.rs | 0 {lib/src/sql => core/src/sql/v1}/value/inc.rs | 0 .../src/sql/v1}/value/increment.rs | 0 .../src/sql => core/src/sql/v1}/value/last.rs | 0 .../sql => core/src/sql/v1}/value/merge.rs | 0 {lib/src/sql => core/src/sql/v1}/value/mod.rs | 0 .../sql => core/src/sql/v1}/value/patch.rs | 0 .../src/sql => core/src/sql/v1}/value/pick.rs | 0 {lib/src/sql => core/src/sql/v1}/value/put.rs | 0 .../sql => core/src/sql/v1}/value/replace.rs | 0 {lib/src/sql => core/src/sql/v1}/value/rid.rs | 0 core/src/sql/v1/value/serde/de/mod.rs | 698 ++++ core/src/sql/v1/value/serde/mod.rs | 5 + .../sql/v1}/value/serde/ser/algorithm/mod.rs | 0 .../src/sql/v1}/value/serde/ser/base/mod.rs | 0 .../src/sql/v1}/value/serde/ser/base/opt.rs | 0 .../v1}/value/serde/ser/block/entry/mod.rs | 0 .../v1}/value/serde/ser/block/entry/vec.rs | 0 .../src/sql/v1}/value/serde/ser/block/mod.rs | 0 .../src/sql/v1}/value/serde/ser/cast/mod.rs | 0 .../sql/v1}/value/serde/ser/changefeed/mod.rs | 0 .../sql/v1}/value/serde/ser/changefeed/opt.rs | 0 .../src/sql/v1}/value/serde/ser/cond/mod.rs | 0 .../src/sql/v1}/value/serde/ser/cond/opt.rs | 0 .../sql/v1}/value/serde/ser/constant/mod.rs | 0 .../src/sql/v1}/value/serde/ser/data/mod.rs | 0 .../src/sql/v1}/value/serde/ser/data/opt.rs | 0 .../sql/v1}/value/serde/ser/datetime/mod.rs | 0 .../sql/v1}/value/serde/ser/decimal/mod.rs | 0 .../src/sql/v1}/value/serde/ser/dir/mod.rs | 0 .../sql/v1}/value/serde/ser/distance/mod.rs | 0 .../sql/v1}/value/serde/ser/duration/mod.rs | 0 .../sql/v1}/value/serde/ser/duration/opt.rs | 0 .../src/sql/v1}/value/serde/ser/edges/mod.rs | 0 .../sql/v1}/value/serde/ser/explain/mod.rs | 0 .../sql/v1}/value/serde/ser/explain/opt.rs | 0 .../sql/v1}/value/serde/ser/expression/mod.rs | 0 .../src/sql/v1}/value/serde/ser/fetch/mod.rs | 0 .../sql/v1}/value/serde/ser/fetch/vec/mod.rs | 0 .../sql/v1}/value/serde/ser/fetch/vec/opt.rs | 0 .../src/sql/v1}/value/serde/ser/fetchs/mod.rs | 0 .../src/sql/v1}/value/serde/ser/fetchs/opt.rs | 0 .../src/sql/v1}/value/serde/ser/field/mod.rs | 0 .../src/sql/v1}/value/serde/ser/field/vec.rs | 0 .../src/sql/v1}/value/serde/ser/fields/mod.rs | 0 .../src/sql/v1}/value/serde/ser/filter/mod.rs | 0 .../sql/v1}/value/serde/ser/filter/vec/mod.rs | 0 .../sql/v1}/value/serde/ser/filter/vec/opt.rs | 0 .../sql/v1}/value/serde/ser/function/mod.rs | 0 .../v1}/value/serde/ser/geometry/coord/mod.rs | 0 .../v1}/value/serde/ser/geometry/coord/vec.rs | 0 .../serde/ser/geometry/line_string/mod.rs | 0 .../serde/ser/geometry/line_string/vec.rs | 0 .../sql/v1}/value/serde/ser/geometry/mod.rs | 0 .../v1}/value/serde/ser/geometry/point/mod.rs | 0 .../v1}/value/serde/ser/geometry/point/vec.rs | 0 .../value/serde/ser/geometry/polygon/mod.rs | 0 .../value/serde/ser/geometry/polygon/vec.rs | 0 .../sql/v1}/value/serde/ser/geometry/vec.rs | 0 .../src/sql/v1}/value/serde/ser/graph/mod.rs | 0 .../src/sql/v1}/value/serde/ser/group/mod.rs | 0 .../sql/v1}/value/serde/ser/group/vec/mod.rs | 0 .../sql/v1}/value/serde/ser/group/vec/opt.rs | 0 .../src/sql/v1}/value/serde/ser/id/mod.rs | 0 .../src/sql/v1}/value/serde/ser/ident/mod.rs | 0 .../src/sql/v1}/value/serde/ser/ident/vec.rs | 0 .../src/sql/v1}/value/serde/ser/idiom/mod.rs | 0 .../sql/v1}/value/serde/ser/idiom/vec/mod.rs | 0 .../sql/v1}/value/serde/ser/idiom/vec/opt.rs | 0 .../src/sql/v1}/value/serde/ser/index/mod.rs | 0 .../v1}/value/serde/ser/index/mtreeparams.rs | 0 .../v1}/value/serde/ser/index/searchparams.rs | 0 .../src/sql/v1}/value/serde/ser/kind/mod.rs | 0 .../src/sql/v1}/value/serde/ser/kind/opt.rs | 0 .../src/sql/v1}/value/serde/ser/kind/vec.rs | 0 .../sql/v1}/value/serde/ser/language/mod.rs | 0 .../src/sql/v1}/value/serde/ser/limit/mod.rs | 0 .../src/sql/v1}/value/serde/ser/limit/opt.rs | 0 .../src/sql/v1}/value/serde/ser/mock/mod.rs | 0 .../src/sql/v1}/value/serde/ser/mod.rs | 0 .../src/sql/v1}/value/serde/ser/number/mod.rs | 0 .../sql/v1}/value/serde/ser/operator/mod.rs | 0 .../src/sql/v1}/value/serde/ser/order/mod.rs | 0 .../sql/v1}/value/serde/ser/order/vec/mod.rs | 0 .../sql/v1}/value/serde/ser/order/vec/opt.rs | 0 .../src/sql/v1}/value/serde/ser/output/mod.rs | 0 .../src/sql/v1}/value/serde/ser/output/opt.rs | 0 .../src/sql/v1}/value/serde/ser/part/mod.rs | 0 .../sql/v1}/value/serde/ser/part/vec/mod.rs | 0 .../sql/v1}/value/serde/ser/part/vec/opt.rs | 0 .../sql/v1}/value/serde/ser/permission/mod.rs | 0 .../v1}/value/serde/ser/permissions/mod.rs | 0 .../sql/v1}/value/serde/ser/primitive/bool.rs | 0 .../sql/v1}/value/serde/ser/primitive/f32.rs | 0 .../sql/v1}/value/serde/ser/primitive/f64.rs | 0 .../sql/v1}/value/serde/ser/primitive/i64.rs | 0 .../sql/v1}/value/serde/ser/primitive/mod.rs | 0 .../v1}/value/serde/ser/primitive/opt/mod.rs | 0 .../v1}/value/serde/ser/primitive/opt/u32.rs | 0 .../v1}/value/serde/ser/primitive/opt/u64.rs | 0 .../sql/v1}/value/serde/ser/primitive/u16.rs | 0 .../sql/v1}/value/serde/ser/primitive/u32.rs | 0 .../sql/v1}/value/serde/ser/primitive/u64.rs | 0 .../sql/v1}/value/serde/ser/primitive/u8.rs | 0 .../sql/v1}/value/serde/ser/range/bound.rs | 0 .../src/sql/v1}/value/serde/ser/range/mod.rs | 0 .../sql/v1}/value/serde/ser/scoring/mod.rs | 0 .../src/sql/v1}/value/serde/ser/split/mod.rs | 0 .../sql/v1}/value/serde/ser/split/vec/mod.rs | 0 .../sql/v1}/value/serde/ser/split/vec/opt.rs | 0 .../src/sql/v1}/value/serde/ser/start/mod.rs | 0 .../src/sql/v1}/value/serde/ser/start/opt.rs | 0 .../v1}/value/serde/ser/statement/analyze.rs | 0 .../v1}/value/serde/ser/statement/begin.rs | 0 .../v1}/value/serde/ser/statement/break.rs | 0 .../v1}/value/serde/ser/statement/cancel.rs | 0 .../v1}/value/serde/ser/statement/commit.rs | 0 .../v1}/value/serde/ser/statement/continue.rs | 0 .../v1}/value/serde/ser/statement/create.rs | 0 .../serde/ser/statement/define/analyzer.rs | 0 .../serde/ser/statement/define/database.rs | 0 .../value/serde/ser/statement/define/event.rs | 0 .../value/serde/ser/statement/define/field.rs | 0 .../serde/ser/statement/define/function.rs | 0 .../value/serde/ser/statement/define/index.rs | 0 .../value/serde/ser/statement/define/mod.rs | 0 .../serde/ser/statement/define/namespace.rs | 0 .../value/serde/ser/statement/define/param.rs | 0 .../value/serde/ser/statement/define/scope.rs | 0 .../value/serde/ser/statement/define/table.rs | 0 .../value/serde/ser/statement/define/token.rs | 0 .../value/serde/ser/statement/define/user.rs | 0 .../v1}/value/serde/ser/statement/delete.rs | 0 .../v1}/value/serde/ser/statement/ifelse.rs | 0 .../sql/v1}/value/serde/ser/statement/info.rs | 0 .../v1}/value/serde/ser/statement/insert.rs | 0 .../sql/v1}/value/serde/ser/statement/kill.rs | 0 .../sql/v1}/value/serde/ser/statement/live.rs | 0 .../sql/v1}/value/serde/ser/statement/mod.rs | 0 .../v1}/value/serde/ser/statement/option.rs | 0 .../v1}/value/serde/ser/statement/output.rs | 0 .../v1}/value/serde/ser/statement/relate.rs | 0 .../serde/ser/statement/remove/analyzer.rs | 0 .../serde/ser/statement/remove/database.rs | 0 .../value/serde/ser/statement/remove/event.rs | 0 .../value/serde/ser/statement/remove/field.rs | 0 .../serde/ser/statement/remove/function.rs | 0 .../value/serde/ser/statement/remove/index.rs | 0 .../value/serde/ser/statement/remove/mod.rs | 0 .../serde/ser/statement/remove/namespace.rs | 0 .../value/serde/ser/statement/remove/param.rs | 0 .../value/serde/ser/statement/remove/scope.rs | 0 .../value/serde/ser/statement/remove/table.rs | 0 .../value/serde/ser/statement/remove/token.rs | 0 .../value/serde/ser/statement/remove/user.rs | 0 .../v1}/value/serde/ser/statement/select.rs | 0 .../sql/v1}/value/serde/ser/statement/set.rs | 0 .../v1}/value/serde/ser/statement/show/mod.rs | 0 .../value/serde/ser/statement/show/since.rs | 0 .../v1}/value/serde/ser/statement/sleep.rs | 0 .../v1}/value/serde/ser/statement/throw.rs | 0 .../v1}/value/serde/ser/statement/update.rs | 0 .../sql/v1}/value/serde/ser/statement/vec.rs | 0 .../sql/v1}/value/serde/ser/statement/yuse.rs | 0 .../src/sql/v1}/value/serde/ser/strand/mod.rs | 0 .../src/sql/v1}/value/serde/ser/strand/opt.rs | 0 .../src/sql/v1}/value/serde/ser/string/mod.rs | 0 .../src/sql/v1}/value/serde/ser/string/opt.rs | 0 .../src/sql/v1}/value/serde/ser/string/vec.rs | 0 .../sql/v1}/value/serde/ser/subquery/mod.rs | 0 .../src/sql/v1}/value/serde/ser/table/mod.rs | 0 .../src/sql/v1}/value/serde/ser/table/opt.rs | 0 .../src/sql/v1}/value/serde/ser/table/vec.rs | 0 .../src/sql/v1}/value/serde/ser/thing/mod.rs | 0 .../sql/v1}/value/serde/ser/timeout/mod.rs | 0 .../sql/v1}/value/serde/ser/timeout/opt.rs | 0 .../sql/v1}/value/serde/ser/tokenizer/mod.rs | 0 .../v1}/value/serde/ser/tokenizer/vec/mod.rs | 0 .../v1}/value/serde/ser/tokenizer/vec/opt.rs | 0 .../src/sql/v1}/value/serde/ser/uuid/mod.rs | 0 .../src/sql/v1}/value/serde/ser/uuid/opt.rs | 0 .../src/sql/v1}/value/serde/ser/value/map.rs | 0 .../src/sql/v1}/value/serde/ser/value/mod.rs | 0 .../src/sql/v1}/value/serde/ser/value/opt.rs | 0 .../src/sql/v1}/value/serde/ser/value/vec.rs | 0 .../sql/v1}/value/serde/ser/vectortype/mod.rs | 0 .../sql/v1}/value/serde/ser/version/mod.rs | 0 .../sql/v1}/value/serde/ser/version/opt.rs | 0 .../src/sql/v1}/value/serde/ser/view/mod.rs | 0 .../src/sql/v1}/value/serde/ser/view/opt.rs | 0 .../src/sql/v1}/value/serde/ser/with/mod.rs | 0 .../src/sql/v1}/value/serde/ser/with/opt.rs | 0 {lib/src/sql => core/src/sql/v1}/value/set.rs | 0 .../sql => core/src/sql/v1}/value/value.rs | 6 +- .../src/sql => core/src/sql/v1}/value/walk.rs | 0 {lib/src/sql => core/src/sql/v1}/version.rs | 0 {lib/src/sql => core/src/sql/v1}/view.rs | 0 {lib/src/sql => core/src/sql/v1}/with.rs | 0 core/src/sql/v2/algorithm.rs | 50 + core/src/sql/v2/arbitrary.rs | 46 + core/src/sql/v2/array.rs | 482 +++ core/src/sql/v2/base.rs | 31 + core/src/sql/v2/block.rs | 242 ++ core/src/sql/v2/bytes.rs | 95 + core/src/sql/v2/cast.rs | 53 + core/src/sql/v2/changefeed.rs | 27 + core/src/sql/v2/cond.rs | 23 + core/src/sql/v2/constant.rs | 119 + core/src/sql/v2/data.rs | 109 + core/src/sql/v2/datetime.rs | 99 + core/src/sql/v2/dir.rs | 28 + core/src/sql/v2/duration.rs | 291 ++ core/src/sql/v2/edges.rs | 28 + core/src/sql/v2/ending.rs | 131 + core/src/sql/v2/escape.rs | 154 + core/src/sql/v2/explain.rs | 18 + core/src/sql/v2/expression.rs | 208 ++ core/src/sql/v2/fetch.rs | 50 + core/src/sql/v2/field.rs | 271 ++ core/src/sql/v2/filter.rs | 30 + core/src/sql/v2/fmt.rs | 313 ++ core/src/sql/v2/function.rs | 266 ++ core/src/sql/v2/future.rs | 46 + core/src/sql/v2/geometry.rs | 611 ++++ core/src/sql/v2/graph.rs | 76 + core/src/sql/v2/group.rs | 54 + core/src/sql/v2/id.rs | 206 ++ core/src/sql/v2/ident.rs | 59 + core/src/sql/v2/idiom.rs | 192 ++ core/src/sql/v2/index.rs | 168 + core/src/sql/v2/kind.rs | 131 + core/src/sql/v2/language.rs | 57 + core/src/sql/v2/limit.rs | 41 + core/src/sql/v2/mock.rs | 78 + core/src/sql/v2/mod.rs | 154 + core/src/sql/v2/model.rs | 211 ++ core/src/sql/v2/number.rs | 739 +++++ core/src/sql/v2/object.rs | 322 ++ core/src/sql/v2/operation.rs | 38 + core/src/sql/v2/operator.rs | 150 + core/src/sql/v2/order.rs | 71 + core/src/sql/v2/output.rs | 36 + core/src/sql/v2/param.rs | 119 + core/src/sql/v2/part.rs | 126 + core/src/sql/v2/paths.rs | 26 + core/src/sql/v2/permission.rs | 166 + core/src/sql/v2/query.rs | 51 + core/src/sql/v2/range.rs | 141 + core/src/sql/v2/regex.rs | 150 + core/src/sql/v2/scoring.rs | 72 + core/src/sql/v2/script.rs | 36 + core/src/sql/v2/serde.rs | 28 + core/src/sql/v2/split.rs | 50 + core/src/sql/v2/start.rs | 41 + core/src/sql/v2/statement.rs | 203 ++ core/src/sql/v2/statements/analyze.rs | 95 + core/src/sql/v2/statements/begin.rs | 15 + core/src/sql/v2/statements/break.rs | 37 + core/src/sql/v2/statements/cancel.rs | 15 + core/src/sql/v2/statements/commit.rs | 15 + core/src/sql/v2/statements/continue.rs | 37 + core/src/sql/v2/statements/create.rs | 93 + core/src/sql/v2/statements/define/analyzer.rs | 69 + core/src/sql/v2/statements/define/database.rs | 66 + core/src/sql/v2/statements/define/event.rs | 64 + core/src/sql/v2/statements/define/field.rs | 138 + core/src/sql/v2/statements/define/function.rs | 74 + core/src/sql/v2/statements/define/index.rs | 82 + core/src/sql/v2/statements/define/mod.rs | 125 + core/src/sql/v2/statements/define/model.rs | 68 + .../src/sql/v2/statements/define/namespace.rs | 59 + core/src/sql/v2/statements/define/param.rs | 68 + core/src/sql/v2/statements/define/scope.rs | 74 + core/src/sql/v2/statements/define/table.rs | 128 + core/src/sql/v2/statements/define/token.rs | 95 + core/src/sql/v2/statements/define/user.rs | 146 + core/src/sql/v2/statements/delete.rs | 93 + core/src/sql/v2/statements/foreach.rs | 99 + core/src/sql/v2/statements/ifelse.rs | 144 + core/src/sql/v2/statements/info.rs | 235 ++ core/src/sql/v2/statements/insert.rs | 123 + core/src/sql/v2/statements/kill.rs | 96 + core/src/sql/v2/statements/live.rs | 137 + core/src/sql/v2/statements/mod.rs | 65 + core/src/sql/v2/statements/option.rs | 23 + core/src/sql/v2/statements/output.rs | 56 + core/src/sql/v2/statements/relate.rs | 196 ++ core/src/sql/v2/statements/remove/analyzer.rs | 44 + core/src/sql/v2/statements/remove/database.rs | 47 + core/src/sql/v2/statements/remove/event.rs | 48 + core/src/sql/v2/statements/remove/field.rs | 49 + core/src/sql/v2/statements/remove/function.rs | 45 + core/src/sql/v2/statements/remove/index.rs | 53 + core/src/sql/v2/statements/remove/mod.rs | 107 + core/src/sql/v2/statements/remove/model.rs | 48 + .../src/sql/v2/statements/remove/namespace.rs | 48 + core/src/sql/v2/statements/remove/param.rs | 44 + core/src/sql/v2/statements/remove/scope.rs | 47 + core/src/sql/v2/statements/remove/table.rs | 77 + core/src/sql/v2/statements/remove/token.rs | 73 + core/src/sql/v2/statements/remove/user.rs | 73 + core/src/sql/v2/statements/select.rs | 214 ++ core/src/sql/v2/statements/set.rs | 50 + core/src/sql/v2/statements/show.rs | 84 + core/src/sql/v2/statements/sleep.rs | 68 + core/src/sql/v2/statements/throw.rs | 39 + core/src/sql/v2/statements/update.rs | 97 + core/src/sql/v2/statements/use.rs | 29 + core/src/sql/v2/strand.rs | 147 + core/src/sql/v2/subquery.rs | 107 + core/src/sql/v2/table.rs | 78 + core/src/sql/v2/thing.rs | 113 + core/src/sql/v2/timeout.rs | 23 + core/src/sql/v2/tokenizer.rs | 25 + core/src/sql/v2/uuid.rs | 92 + core/src/sql/v2/value/all.rs | 8 + core/src/sql/v2/value/changed.rs | 94 + core/src/sql/v2/value/clear.rs | 24 + core/src/sql/v2/value/compare.rs | 204 ++ core/src/sql/v2/value/cut.rs | 188 ++ core/src/sql/v2/value/dec.rs | 79 + core/src/sql/v2/value/decrement.rs | 95 + core/src/sql/v2/value/def.rs | 9 + core/src/sql/v2/value/del.rs | 337 ++ core/src/sql/v2/value/diff.rs | 132 + core/src/sql/v2/value/each.rs | 142 + core/src/sql/v2/value/every.rs | 149 + core/src/sql/v2/value/extend.rs | 58 + core/src/sql/v2/value/fetch.rs | 148 + core/src/sql/v2/value/first.rs | 8 + core/src/sql/v2/value/flatten.rs | 19 + core/src/sql/v2/value/generate.rs | 69 + core/src/sql/v2/value/get.rs | 433 +++ core/src/sql/v2/value/inc.rs | 79 + core/src/sql/v2/value/increment.rs | 96 + core/src/sql/v2/value/last.rs | 8 + core/src/sql/v2/value/merge.rs | 82 + core/src/sql/v2/value/mod.rs | 35 + core/src/sql/v2/value/patch.rs | 238 ++ core/src/sql/v2/value/pick.rs | 134 + core/src/sql/v2/value/put.rs | 199 ++ core/src/sql/v2/value/replace.rs | 32 + core/src/sql/v2/value/rid.rs | 34 + core/src/sql/v2/value/serde/de/mod.rs | 698 ++++ core/src/sql/v2/value/serde/mod.rs | 5 + .../sql/v2/value/serde/ser/algorithm/mod.rs | 145 + core/src/sql/v2/value/serde/ser/base/mod.rs | 94 + core/src/sql/v2/value/serde/ser/base/opt.rs | 55 + .../sql/v2/value/serde/ser/block/entry/mod.rs | 157 + .../sql/v2/value/serde/ser/block/entry/vec.rs | 77 + core/src/sql/v2/value/serde/ser/block/mod.rs | 1 + core/src/sql/v2/value/serde/ser/cast/mod.rs | 85 + .../sql/v2/value/serde/ser/changefeed/mod.rs | 79 + .../sql/v2/value/serde/ser/changefeed/opt.rs | 55 + core/src/sql/v2/value/serde/ser/cond/mod.rs | 1 + core/src/sql/v2/value/serde/ser/cond/opt.rs | 55 + .../sql/v2/value/serde/ser/constant/mod.rs | 193 ++ core/src/sql/v2/value/serde/ser/data/mod.rs | 457 +++ core/src/sql/v2/value/serde/ser/data/opt.rs | 55 + .../sql/v2/value/serde/ser/datetime/mod.rs | 59 + .../src/sql/v2/value/serde/ser/decimal/mod.rs | 41 + core/src/sql/v2/value/serde/ser/dir/mod.rs | 65 + .../sql/v2/value/serde/ser/distance/mod.rs | 94 + .../sql/v2/value/serde/ser/duration/mod.rs | 96 + .../sql/v2/value/serde/ser/duration/opt.rs | 55 + core/src/sql/v2/value/serde/ser/edges/mod.rs | 98 + .../src/sql/v2/value/serde/ser/explain/mod.rs | 1 + .../src/sql/v2/value/serde/ser/explain/opt.rs | 74 + .../sql/v2/value/serde/ser/expression/mod.rs | 190 ++ core/src/sql/v2/value/serde/ser/fetch/mod.rs | 1 + .../sql/v2/value/serde/ser/fetch/vec/mod.rs | 80 + .../sql/v2/value/serde/ser/fetch/vec/opt.rs | 55 + core/src/sql/v2/value/serde/ser/fetchs/mod.rs | 1 + core/src/sql/v2/value/serde/ser/fetchs/opt.rs | 55 + core/src/sql/v2/value/serde/ser/field/mod.rs | 156 + core/src/sql/v2/value/serde/ser/field/vec.rs | 77 + core/src/sql/v2/value/serde/ser/fields/mod.rs | 90 + core/src/sql/v2/value/serde/ser/filter/mod.rs | 154 + .../sql/v2/value/serde/ser/filter/vec/mod.rs | 79 + .../sql/v2/value/serde/ser/filter/vec/opt.rs | 55 + .../sql/v2/value/serde/ser/function/mod.rs | 133 + .../v2/value/serde/ser/geometry/coord/mod.rs | 98 + .../v2/value/serde/ser/geometry/coord/vec.rs | 77 + .../serde/ser/geometry/line_string/mod.rs | 1 + .../serde/ser/geometry/line_string/vec.rs | 78 + .../sql/v2/value/serde/ser/geometry/mod.rs | 155 + .../v2/value/serde/ser/geometry/point/mod.rs | 1 + .../v2/value/serde/ser/geometry/point/vec.rs | 77 + .../value/serde/ser/geometry/polygon/mod.rs | 87 + .../value/serde/ser/geometry/polygon/vec.rs | 78 + .../sql/v2/value/serde/ser/geometry/vec.rs | 65 + core/src/sql/v2/value/serde/ser/graph/mod.rs | 155 + core/src/sql/v2/value/serde/ser/group/mod.rs | 1 + .../sql/v2/value/serde/ser/group/vec/mod.rs | 80 + .../sql/v2/value/serde/ser/group/vec/opt.rs | 55 + core/src/sql/v2/value/serde/ser/id/mod.rs | 84 + core/src/sql/v2/value/serde/ser/ident/mod.rs | 1 + core/src/sql/v2/value/serde/ser/ident/vec.rs | 65 + core/src/sql/v2/value/serde/ser/idiom/mod.rs | 1 + .../sql/v2/value/serde/ser/idiom/vec/mod.rs | 79 + .../sql/v2/value/serde/ser/idiom/vec/opt.rs | 55 + core/src/sql/v2/value/serde/ser/index/mod.rs | 104 + .../v2/value/serde/ser/index/mtreeparams.rs | 120 + .../v2/value/serde/ser/index/searchparams.rs | 149 + core/src/sql/v2/value/serde/ser/kind/mod.rs | 302 ++ core/src/sql/v2/value/serde/ser/kind/opt.rs | 55 + core/src/sql/v2/value/serde/ser/kind/vec.rs | 77 + .../sql/v2/value/serde/ser/language/mod.rs | 65 + core/src/sql/v2/value/serde/ser/limit/mod.rs | 1 + core/src/sql/v2/value/serde/ser/limit/opt.rs | 55 + core/src/sql/v2/value/serde/ser/mock/mod.rs | 113 + core/src/sql/v2/value/serde/ser/mod.rs | 501 +++ core/src/sql/v2/value/serde/ser/number/mod.rs | 151 + .../sql/v2/value/serde/ser/operator/mod.rs | 331 ++ core/src/sql/v2/value/serde/ser/order/mod.rs | 105 + .../sql/v2/value/serde/ser/order/vec/mod.rs | 79 + .../sql/v2/value/serde/ser/order/vec/opt.rs | 55 + core/src/sql/v2/value/serde/ser/output/mod.rs | 109 + core/src/sql/v2/value/serde/ser/output/opt.rs | 55 + core/src/sql/v2/value/serde/ser/part/mod.rs | 136 + .../sql/v2/value/serde/ser/part/vec/mod.rs | 79 + .../sql/v2/value/serde/ser/part/vec/opt.rs | 55 + .../sql/v2/value/serde/ser/permission/mod.rs | 83 + .../sql/v2/value/serde/ser/permissions/mod.rs | 92 + .../sql/v2/value/serde/ser/primitive/bool.rs | 25 + .../sql/v2/value/serde/ser/primitive/f32.rs | 25 + .../sql/v2/value/serde/ser/primitive/f64.rs | 25 + .../sql/v2/value/serde/ser/primitive/i64.rs | 25 + .../sql/v2/value/serde/ser/primitive/mod.rs | 10 + .../v2/value/serde/ser/primitive/opt/mod.rs | 2 + .../v2/value/serde/ser/primitive/opt/u32.rs | 54 + .../v2/value/serde/ser/primitive/opt/u64.rs | 54 + .../sql/v2/value/serde/ser/primitive/u16.rs | 25 + .../sql/v2/value/serde/ser/primitive/u32.rs | 27 + .../sql/v2/value/serde/ser/primitive/u64.rs | 27 + .../sql/v2/value/serde/ser/primitive/u8.rs | 25 + .../src/sql/v2/value/serde/ser/range/bound.rs | 84 + core/src/sql/v2/value/serde/ser/range/mod.rs | 98 + .../src/sql/v2/value/serde/ser/scoring/mod.rs | 133 + core/src/sql/v2/value/serde/ser/split/mod.rs | 1 + .../sql/v2/value/serde/ser/split/vec/mod.rs | 80 + .../sql/v2/value/serde/ser/split/vec/opt.rs | 55 + core/src/sql/v2/value/serde/ser/start/mod.rs | 1 + core/src/sql/v2/value/serde/ser/start/opt.rs | 55 + .../v2/value/serde/ser/statement/analyze.rs | 95 + .../sql/v2/value/serde/ser/statement/begin.rs | 44 + .../sql/v2/value/serde/ser/statement/break.rs | 44 + .../v2/value/serde/ser/statement/cancel.rs | 44 + .../v2/value/serde/ser/statement/commit.rs | 44 + .../v2/value/serde/ser/statement/continue.rs | 44 + .../v2/value/serde/ser/statement/create.rs | 141 + .../serde/ser/statement/define/analyzer.rs | 102 + .../serde/ser/statement/define/database.rs | 96 + .../value/serde/ser/statement/define/event.rs | 102 + .../value/serde/ser/statement/define/field.rs | 129 + .../serde/ser/statement/define/function.rs | 202 ++ .../value/serde/ser/statement/define/index.rs | 102 + .../value/serde/ser/statement/define/mod.rs | 164 + .../serde/ser/statement/define/namespace.rs | 90 + .../value/serde/ser/statement/define/param.rs | 97 + .../value/serde/ser/statement/define/scope.rs | 108 + .../value/serde/ser/statement/define/table.rs | 118 + .../value/serde/ser/statement/define/token.rs | 102 + .../value/serde/ser/statement/define/user.rs | 106 + .../v2/value/serde/ser/statement/delete.rs | 138 + .../v2/value/serde/ser/statement/ifelse.rs | 205 ++ .../sql/v2/value/serde/ser/statement/info.rs | 161 + .../v2/value/serde/ser/statement/insert.rs | 143 + .../sql/v2/value/serde/ser/statement/kill.rs | 80 + .../sql/v2/value/serde/ser/statement/live.rs | 122 + .../sql/v2/value/serde/ser/statement/mod.rs | 259 ++ .../v2/value/serde/ser/statement/option.rs | 82 + .../v2/value/serde/ser/statement/output.rs | 96 + .../v2/value/serde/ser/statement/relate.rs | 155 + .../serde/ser/statement/remove/analyzer.rs | 79 + .../serde/ser/statement/remove/database.rs | 79 + .../value/serde/ser/statement/remove/event.rs | 84 + .../value/serde/ser/statement/remove/field.rs | 85 + .../serde/ser/statement/remove/function.rs | 79 + .../value/serde/ser/statement/remove/index.rs | 84 + .../value/serde/ser/statement/remove/mod.rs | 164 + .../serde/ser/statement/remove/namespace.rs | 79 + .../value/serde/ser/statement/remove/param.rs | 79 + .../value/serde/ser/statement/remove/scope.rs | 79 + .../value/serde/ser/statement/remove/table.rs | 84 + .../value/serde/ser/statement/remove/token.rs | 85 + .../value/serde/ser/statement/remove/user.rs | 85 + .../v2/value/serde/ser/statement/select.rs | 298 ++ .../sql/v2/value/serde/ser/statement/set.rs | 85 + .../v2/value/serde/ser/statement/show/mod.rs | 130 + .../value/serde/ser/statement/show/since.rs | 68 + .../sql/v2/value/serde/ser/statement/sleep.rs | 77 + .../sql/v2/value/serde/ser/statement/throw.rs | 77 + .../v2/value/serde/ser/statement/update.rs | 157 + .../sql/v2/value/serde/ser/statement/vec.rs | 78 + .../sql/v2/value/serde/ser/statement/yuse.rs | 111 + core/src/sql/v2/value/serde/ser/strand/mod.rs | 1 + core/src/sql/v2/value/serde/ser/strand/opt.rs | 55 + core/src/sql/v2/value/serde/ser/string/mod.rs | 79 + core/src/sql/v2/value/serde/ser/string/opt.rs | 54 + core/src/sql/v2/value/serde/ser/string/vec.rs | 76 + .../sql/v2/value/serde/ser/subquery/mod.rs | 136 + core/src/sql/v2/value/serde/ser/table/mod.rs | 2 + core/src/sql/v2/value/serde/ser/table/opt.rs | 55 + core/src/sql/v2/value/serde/ser/table/vec.rs | 77 + core/src/sql/v2/value/serde/ser/thing/mod.rs | 90 + .../src/sql/v2/value/serde/ser/timeout/mod.rs | 1 + .../src/sql/v2/value/serde/ser/timeout/opt.rs | 56 + .../sql/v2/value/serde/ser/tokenizer/mod.rs | 75 + .../v2/value/serde/ser/tokenizer/vec/mod.rs | 79 + .../v2/value/serde/ser/tokenizer/vec/opt.rs | 55 + core/src/sql/v2/value/serde/ser/uuid/mod.rs | 90 + core/src/sql/v2/value/serde/ser/uuid/opt.rs | 55 + core/src/sql/v2/value/serde/ser/value/map.rs | 99 + core/src/sql/v2/value/serde/ser/value/mod.rs | 933 ++++++ core/src/sql/v2/value/serde/ser/value/opt.rs | 55 + core/src/sql/v2/value/serde/ser/value/vec.rs | 78 + .../sql/v2/value/serde/ser/vectortype/mod.rs | 79 + .../src/sql/v2/value/serde/ser/version/mod.rs | 1 + .../src/sql/v2/value/serde/ser/version/opt.rs | 56 + core/src/sql/v2/value/serde/ser/view/mod.rs | 97 + core/src/sql/v2/value/serde/ser/view/opt.rs | 55 + core/src/sql/v2/value/serde/ser/with/mod.rs | 78 + core/src/sql/v2/value/serde/ser/with/opt.rs | 55 + core/src/sql/v2/value/set.rs | 323 ++ core/src/sql/v2/value/value.rs | 2904 +++++++++++++++++ core/src/sql/v2/value/walk.rs | 170 + core/src/sql/v2/version.rs | 15 + core/src/sql/v2/view.rs | 27 + core/src/sql/v2/with.rs | 24 + {lib => core}/src/syn/common.rs | 0 {lib => core}/src/syn/error/mod.rs | 0 {lib => core}/src/syn/error/nom_error.rs | 0 {lib => core}/src/syn/mod.rs | 0 {lib => core}/src/syn/v1/block.rs | 0 {lib => core}/src/syn/v1/builtin.rs | 0 {lib => core}/src/syn/v1/comment.rs | 0 {lib => core}/src/syn/v1/common.rs | 0 {lib => core}/src/syn/v1/depth.rs | 0 {lib => core}/src/syn/v1/ending.rs | 0 {lib => core}/src/syn/v1/error.rs | 0 {lib => core}/src/syn/v1/expression.rs | 0 {lib => core}/src/syn/v1/function.rs | 0 {lib => core}/src/syn/v1/idiom.rs | 0 {lib => core}/src/syn/v1/kind.rs | 0 {lib => core}/src/syn/v1/literal/algorithm.rs | 0 {lib => core}/src/syn/v1/literal/datetime.rs | 0 {lib => core}/src/syn/v1/literal/duration.rs | 0 {lib => core}/src/syn/v1/literal/filter.rs | 0 {lib => core}/src/syn/v1/literal/language.rs | 0 {lib => core}/src/syn/v1/literal/mod.rs | 0 {lib => core}/src/syn/v1/literal/number.rs | 0 {lib => core}/src/syn/v1/literal/range.rs | 0 {lib => core}/src/syn/v1/literal/regex.rs | 0 {lib => core}/src/syn/v1/literal/scoring.rs | 0 {lib => core}/src/syn/v1/literal/strand.rs | 0 {lib => core}/src/syn/v1/literal/tokenizer.rs | 0 {lib => core}/src/syn/v1/literal/uuid.rs | 0 {lib => core}/src/syn/v1/mod.rs | 0 {lib => core}/src/syn/v1/omit.rs | 0 {lib => core}/src/syn/v1/operator.rs | 0 {lib => core}/src/syn/v1/part/data.rs | 0 {lib => core}/src/syn/v1/part/field.rs | 0 {lib => core}/src/syn/v1/part/index.rs | 0 {lib => core}/src/syn/v1/part/mod.rs | 0 {lib => core}/src/syn/v1/part/permission.rs | 0 {lib => core}/src/syn/v1/part/split.rs | 0 {lib => core}/src/syn/v1/part/start.rs | 0 {lib => core}/src/syn/v1/part/timeout.rs | 0 {lib => core}/src/syn/v1/part/view.rs | 0 {lib => core}/src/syn/v1/part/with.rs | 0 {lib => core}/src/syn/v1/special.rs | 0 {lib => core}/src/syn/v1/stmt/analyze.rs | 0 {lib => core}/src/syn/v1/stmt/begin.rs | 0 {lib => core}/src/syn/v1/stmt/cancel.rs | 0 {lib => core}/src/syn/v1/stmt/commit.rs | 0 {lib => core}/src/syn/v1/stmt/create.rs | 0 .../src/syn/v1/stmt/define/analyzer.rs | 0 .../src/syn/v1/stmt/define/database.rs | 0 {lib => core}/src/syn/v1/stmt/define/event.rs | 0 {lib => core}/src/syn/v1/stmt/define/field.rs | 0 .../src/syn/v1/stmt/define/function.rs | 0 {lib => core}/src/syn/v1/stmt/define/index.rs | 0 {lib => core}/src/syn/v1/stmt/define/mod.rs | 0 .../src/syn/v1/stmt/define/namespace.rs | 0 {lib => core}/src/syn/v1/stmt/define/param.rs | 0 {lib => core}/src/syn/v1/stmt/define/scope.rs | 0 {lib => core}/src/syn/v1/stmt/define/table.rs | 0 {lib => core}/src/syn/v1/stmt/define/token.rs | 0 {lib => core}/src/syn/v1/stmt/define/user.rs | 0 {lib => core}/src/syn/v1/stmt/delete.rs | 0 {lib => core}/src/syn/v1/stmt/flow.rs | 0 {lib => core}/src/syn/v1/stmt/foreach.rs | 0 {lib => core}/src/syn/v1/stmt/ifelse.rs | 0 {lib => core}/src/syn/v1/stmt/info.rs | 0 {lib => core}/src/syn/v1/stmt/insert.rs | 0 {lib => core}/src/syn/v1/stmt/kill.rs | 0 {lib => core}/src/syn/v1/stmt/live.rs | 0 {lib => core}/src/syn/v1/stmt/mod.rs | 0 {lib => core}/src/syn/v1/stmt/option.rs | 0 {lib => core}/src/syn/v1/stmt/output.rs | 0 {lib => core}/src/syn/v1/stmt/relate.rs | 0 {lib => core}/src/syn/v1/stmt/remove.rs | 0 {lib => core}/src/syn/v1/stmt/select.rs | 0 {lib => core}/src/syn/v1/stmt/set.rs | 0 {lib => core}/src/syn/v1/stmt/show.rs | 0 {lib => core}/src/syn/v1/stmt/sleep.rs | 0 {lib => core}/src/syn/v1/stmt/throw.rs | 0 {lib => core}/src/syn/v1/stmt/update.rs | 0 {lib => core}/src/syn/v1/stmt/use.rs | 0 {lib => core}/src/syn/v1/subquery.rs | 0 {lib => core}/src/syn/v1/test.rs | 0 {lib => core}/src/syn/v1/thing.rs | 0 {lib => core}/src/syn/v1/value/geometry.rs | 0 {lib => core}/src/syn/v1/value/mock.rs | 0 {lib => core}/src/syn/v1/value/mod.rs | 0 {lib => core}/src/syn/v2/lexer/byte.rs | 0 {lib => core}/src/syn/v2/lexer/char.rs | 0 {lib => core}/src/syn/v2/lexer/datetime.rs | 0 {lib => core}/src/syn/v2/lexer/duration.rs | 0 {lib => core}/src/syn/v2/lexer/ident.rs | 0 {lib => core}/src/syn/v2/lexer/js.rs | 0 {lib => core}/src/syn/v2/lexer/keywords.rs | 0 {lib => core}/src/syn/v2/lexer/mod.rs | 0 {lib => core}/src/syn/v2/lexer/number.rs | 0 {lib => core}/src/syn/v2/lexer/reader.rs | 0 {lib => core}/src/syn/v2/lexer/strand.rs | 0 {lib => core}/src/syn/v2/lexer/test.rs | 0 {lib => core}/src/syn/v2/lexer/unicode.rs | 0 {lib => core}/src/syn/v2/lexer/uuid.rs | 0 {lib => core}/src/syn/v2/mod.rs | 0 {lib => core}/src/syn/v2/parser/basic.rs | 0 {lib => core}/src/syn/v2/parser/builtin.rs | 0 {lib => core}/src/syn/v2/parser/error.rs | 0 {lib => core}/src/syn/v2/parser/expression.rs | 0 {lib => core}/src/syn/v2/parser/function.rs | 0 {lib => core}/src/syn/v2/parser/idiom.rs | 0 {lib => core}/src/syn/v2/parser/json.rs | 0 {lib => core}/src/syn/v2/parser/kind.rs | 0 {lib => core}/src/syn/v2/parser/mac.rs | 0 {lib => core}/src/syn/v2/parser/mod.rs | 0 {lib => core}/src/syn/v2/parser/object.rs | 0 {lib => core}/src/syn/v2/parser/prime.rs | 0 .../src/syn/v2/parser/stmt/create.rs | 0 .../src/syn/v2/parser/stmt/define.rs | 0 .../src/syn/v2/parser/stmt/delete.rs | 0 {lib => core}/src/syn/v2/parser/stmt/if.rs | 0 .../src/syn/v2/parser/stmt/insert.rs | 0 {lib => core}/src/syn/v2/parser/stmt/mod.rs | 0 {lib => core}/src/syn/v2/parser/stmt/parts.rs | 0 .../src/syn/v2/parser/stmt/relate.rs | 0 .../src/syn/v2/parser/stmt/remove.rs | 0 .../src/syn/v2/parser/stmt/select.rs | 0 .../src/syn/v2/parser/stmt/update.rs | 0 {lib => core}/src/syn/v2/parser/test/mod.rs | 0 {lib => core}/src/syn/v2/parser/test/stmt.rs | 0 .../src/syn/v2/parser/test/streaming.rs | 0 {lib => core}/src/syn/v2/parser/test/value.rs | 0 {lib => core}/src/syn/v2/parser/thing.rs | 0 .../src/syn/v2/parser/token_buffer.rs | 0 {lib => core}/src/syn/v2/test.rs | 0 {lib => core}/src/syn/v2/token/keyword.rs | 0 {lib => core}/src/syn/v2/token/mac.rs | 0 {lib => core}/src/syn/v2/token/mod.rs | 0 {lib => core}/src/vs/conv.rs | 0 {lib => core}/src/vs/mod.rs | 0 {lib => core}/src/vs/oracle.rs | 0 {lib => core}/test.surql | 0 deny.toml | 14 +- lib/Cargo.toml | 118 +- lib/benches/parser.rs | 2 +- lib/benches/sdb_benches/lib/mod.rs | 1 + lib/src/api/conn.rs | 2 +- lib/src/api/engine/any/mod.rs | 4 +- lib/src/api/engine/local/mod.rs | 8 +- lib/src/api/engine/local/native.rs | 3 +- lib/src/api/engine/local/wasm.rs | 3 +- lib/src/api/engine/remote/http/mod.rs | 2 +- lib/src/api/err/mod.rs | 10 + lib/src/api/method/live.rs | 30 +- lib/src/api/mod.rs | 18 +- lib/src/api/opt/capabilities.rs | 64 +- lib/src/api/opt/mod.rs | 689 +--- lib/src/api/opt/query.rs | 3 +- lib/src/lib.rs | 49 +- lib/src/sql/value/serde/mod.rs | 3 - lib/tests/remove.rs | 1 + src/dbs/mod.rs | 12 +- src/net/ml.rs | 2 +- supply-chain/audits.toml | 6 + supply-chain/config.toml | 7 + supply-chain/imports.lock | 7 + tests/ml_integration.rs | 2 +- 1158 files changed, 37701 insertions(+), 1033 deletions(-) create mode 100644 core/Cargo.toml rename {lib => core}/src/cf/gc.rs (100%) rename {lib => core}/src/cf/mod.rs (100%) rename {lib => core}/src/cf/mutations.rs (100%) rename {lib => core}/src/cf/reader.rs (100%) rename {lib => core}/src/cf/writer.rs (100%) rename {lib => core}/src/cnf/mod.rs (97%) rename {lib => core}/src/ctx/cancellation.rs (100%) rename {lib => core}/src/ctx/canceller.rs (100%) rename {lib => core}/src/ctx/context.rs (100%) rename {lib => core}/src/ctx/mod.rs (100%) rename {lib => core}/src/ctx/reason.rs (100%) rename {lib => core}/src/dbs/capabilities.rs (86%) rename {lib => core}/src/dbs/distinct.rs (100%) rename {lib => core}/src/dbs/executor.rs (100%) rename {lib => core}/src/dbs/explanation.rs (100%) rename {lib => core}/src/dbs/iterator.rs (100%) rename {lib => core}/src/dbs/mod.rs (100%) rename {lib => core}/src/dbs/node.rs (100%) rename {lib => core}/src/dbs/notification.rs (100%) rename {lib => core}/src/dbs/options.rs (100%) rename {lib => core}/src/dbs/processor.rs (100%) rename {lib => core}/src/dbs/response.rs (97%) rename {lib => core}/src/dbs/session.rs (100%) rename {lib => core}/src/dbs/statement.rs (100%) rename {lib => core}/src/dbs/test.rs (100%) rename {lib => core}/src/dbs/transaction.rs (100%) rename {lib => core}/src/dbs/variables.rs (100%) rename {lib => core}/src/doc/allow.rs (100%) rename {lib => core}/src/doc/alter.rs (100%) rename {lib => core}/src/doc/changefeeds.rs (100%) rename {lib => core}/src/doc/check.rs (100%) rename {lib => core}/src/doc/clean.rs (100%) rename {lib => core}/src/doc/compute.rs (100%) rename {lib => core}/src/doc/create.rs (100%) rename {lib => core}/src/doc/delete.rs (100%) rename {lib => core}/src/doc/document.rs (100%) rename {lib => core}/src/doc/edges.rs (100%) rename {lib => core}/src/doc/empty.rs (100%) rename {lib => core}/src/doc/erase.rs (100%) rename {lib => core}/src/doc/event.rs (100%) rename {lib => core}/src/doc/field.rs (100%) rename {lib => core}/src/doc/index.rs (100%) rename {lib => core}/src/doc/insert.rs (100%) rename {lib => core}/src/doc/lives.rs (100%) rename {lib => core}/src/doc/merge.rs (100%) rename {lib => core}/src/doc/mod.rs (100%) rename {lib => core}/src/doc/pluck.rs (100%) rename {lib => core}/src/doc/process.rs (100%) rename {lib => core}/src/doc/purge.rs (100%) rename {lib => core}/src/doc/relate.rs (100%) rename {lib => core}/src/doc/reset.rs (100%) rename {lib => core}/src/doc/select.rs (100%) rename {lib => core}/src/doc/store.rs (100%) rename {lib => core}/src/doc/table.rs (100%) rename {lib => core}/src/doc/update.rs (100%) rename {lib => core}/src/env/mod.rs (100%) rename {lib => core}/src/err/mod.rs (100%) rename {lib => core}/src/exe/mod.rs (100%) rename {lib => core}/src/exe/spawn.rs (100%) rename {lib => core}/src/exe/try_join_all_buffered.rs (100%) rename {lib => core}/src/fflags.rs (100%) rename {lib => core}/src/fnc/args.rs (100%) rename {lib => core}/src/fnc/array.rs (100%) rename {lib => core}/src/fnc/bytes.rs (100%) rename {lib => core}/src/fnc/count.rs (100%) rename {lib => core}/src/fnc/crypto.rs (100%) rename {lib => core}/src/fnc/duration.rs (100%) rename {lib => core}/src/fnc/encoding.rs (100%) rename {lib => core}/src/fnc/geo.rs (100%) rename {lib => core}/src/fnc/http.rs (100%) rename {lib => core}/src/fnc/math.rs (100%) rename {lib => core}/src/fnc/meta.rs (100%) rename {lib => core}/src/fnc/mod.rs (100%) rename {lib => core}/src/fnc/not.rs (100%) rename {lib => core}/src/fnc/object.rs (100%) rename {lib => core}/src/fnc/operate.rs (100%) rename {lib => core}/src/fnc/parse.rs (100%) rename {lib => core}/src/fnc/rand.rs (100%) rename {lib => core}/src/fnc/script/classes/duration.rs (100%) rename {lib => core}/src/fnc/script/classes/mod.rs (100%) rename {lib => core}/src/fnc/script/classes/record.rs (100%) rename {lib => core}/src/fnc/script/classes/uuid.rs (100%) rename {lib => core}/src/fnc/script/error.rs (100%) rename {lib => core}/src/fnc/script/fetch/body.rs (100%) rename {lib => core}/src/fnc/script/fetch/classes/blob.rs (100%) rename {lib => core}/src/fnc/script/fetch/classes/form_data.rs (100%) rename {lib => core}/src/fnc/script/fetch/classes/headers.rs (100%) rename {lib => core}/src/fnc/script/fetch/classes/mod.rs (100%) rename {lib => core}/src/fnc/script/fetch/classes/request.rs (100%) rename {lib => core}/src/fnc/script/fetch/classes/response/init.rs (100%) rename {lib => core}/src/fnc/script/fetch/classes/response/mod.rs (100%) rename {lib => core}/src/fnc/script/fetch/func.rs (100%) rename {lib => core}/src/fnc/script/fetch/mod.rs (100%) rename {lib => core}/src/fnc/script/fetch/stream.rs (100%) rename {lib => core}/src/fnc/script/fetch/util.rs (100%) rename {lib => core}/src/fnc/script/fetch_stub/mod.rs (100%) rename {lib => core}/src/fnc/script/fetch_stub/test.rs (100%) rename {lib => core}/src/fnc/script/from.rs (100%) rename {lib => core}/src/fnc/script/globals/console.rs (100%) rename {lib => core}/src/fnc/script/globals/mod.rs (100%) rename {lib => core}/src/fnc/script/into.rs (100%) rename {lib => core}/src/fnc/script/main.rs (100%) rename {lib => core}/src/fnc/script/mod.rs (100%) rename {lib => core}/src/fnc/script/modules/mod.rs (100%) rename {lib => core}/src/fnc/script/modules/os.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/array.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/array/sort.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/bytes.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/crypto.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/crypto/argon2.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/crypto/bcrypt.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/crypto/pbkdf2.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/crypto/scrypt.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/duration.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/duration/from.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/encoding.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/encoding/base64.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/geo.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/geo/hash.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/http.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/math.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/meta.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/mod.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/object.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/parse.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/parse/email.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/parse/url.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/rand.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/rand/uuid.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/search.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/session.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/string.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/string/distance.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/string/is.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/string/semver.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/string/semver/inc.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/string/semver/set.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/string/similarity.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/time.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/time/from.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/type.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/type/is.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/vector.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/vector/distance.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/functions/vector/similarity.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/mod.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/query/classes.rs (100%) rename {lib => core}/src/fnc/script/modules/surrealdb/query/mod.rs (100%) rename {lib => core}/src/fnc/script/tests/fetch.rs (100%) rename {lib => core}/src/fnc/script/tests/mod.rs (100%) rename {lib => core}/src/fnc/search.rs (100%) rename {lib => core}/src/fnc/session.rs (100%) rename {lib => core}/src/fnc/sleep.rs (100%) rename {lib => core}/src/fnc/string.rs (100%) rename {lib => core}/src/fnc/time.rs (100%) rename {lib => core}/src/fnc/type.rs (100%) rename {lib => core}/src/fnc/util/geo/mod.rs (100%) rename {lib => core}/src/fnc/util/http/mod.rs (100%) rename {lib => core}/src/fnc/util/math/bottom.rs (100%) rename {lib => core}/src/fnc/util/math/deviation.rs (100%) rename {lib => core}/src/fnc/util/math/interquartile.rs (100%) rename {lib => core}/src/fnc/util/math/mean.rs (100%) rename {lib => core}/src/fnc/util/math/median.rs (100%) rename {lib => core}/src/fnc/util/math/midhinge.rs (100%) rename {lib => core}/src/fnc/util/math/mod.rs (100%) rename {lib => core}/src/fnc/util/math/mode.rs (100%) rename {lib => core}/src/fnc/util/math/nearestrank.rs (100%) rename {lib => core}/src/fnc/util/math/percentile.rs (100%) rename {lib => core}/src/fnc/util/math/quartile.rs (100%) rename {lib => core}/src/fnc/util/math/spread.rs (100%) rename {lib => core}/src/fnc/util/math/top.rs (100%) rename {lib => core}/src/fnc/util/math/trimean.rs (100%) rename {lib => core}/src/fnc/util/math/variance.rs (100%) rename {lib => core}/src/fnc/util/math/vector.rs (100%) rename {lib => core}/src/fnc/util/mod.rs (100%) rename {lib => core}/src/fnc/util/string/fuzzy.rs (100%) rename {lib => core}/src/fnc/util/string/mod.rs (100%) rename {lib => core}/src/fnc/util/string/slug.rs (100%) rename {lib => core}/src/fnc/vector.rs (100%) rename {lib => core}/src/iam/auth.rs (100%) rename {lib => core}/src/iam/base.rs (100%) rename {lib => core}/src/iam/check.rs (100%) rename {lib => core}/src/iam/clear.rs (100%) rename {lib => core}/src/iam/entities/action.rs (100%) rename {lib => core}/src/iam/entities/mod.rs (100%) rename {lib => core}/src/iam/entities/resources/actor.rs (100%) rename {lib => core}/src/iam/entities/resources/level.rs (100%) rename {lib => core}/src/iam/entities/resources/mod.rs (100%) rename {lib => core}/src/iam/entities/resources/resource.rs (100%) rename {lib => core}/src/iam/entities/roles.rs (100%) rename {lib => core}/src/iam/entities/schema.rs (100%) rename {lib => core}/src/iam/jwks.rs (99%) rename {lib => core}/src/iam/mod.rs (100%) rename {lib => core}/src/iam/policies/mod.rs (100%) rename {lib => core}/src/iam/policies/policy_set.rs (100%) rename {lib => core}/src/iam/signin.rs (100%) rename {lib => core}/src/iam/signup.rs (100%) rename {lib => core}/src/iam/token.rs (100%) rename {lib => core}/src/iam/verify.rs (99%) rename {lib => core}/src/idg/mod.rs (100%) rename {lib => core}/src/idg/u32.rs (100%) rename {lib => core}/src/idx/docids.rs (100%) rename {lib => core}/src/idx/ft/analyzer/filter.rs (100%) rename {lib => core}/src/idx/ft/analyzer/mod.rs (100%) rename {lib => core}/src/idx/ft/analyzer/tokenizer.rs (100%) rename {lib => core}/src/idx/ft/doclength.rs (100%) rename {lib => core}/src/idx/ft/highlighter.rs (100%) rename {lib => core}/src/idx/ft/mod.rs (100%) rename {lib => core}/src/idx/ft/offsets.rs (100%) rename {lib => core}/src/idx/ft/postings.rs (100%) rename {lib => core}/src/idx/ft/scorer.rs (100%) rename {lib => core}/src/idx/ft/termdocs.rs (100%) rename {lib => core}/src/idx/ft/terms.rs (100%) rename {lib => core}/src/idx/mod.rs (100%) rename {lib => core}/src/idx/planner/executor.rs (100%) rename {lib => core}/src/idx/planner/iterators.rs (100%) rename {lib => core}/src/idx/planner/knn.rs (100%) rename {lib => core}/src/idx/planner/mod.rs (100%) rename {lib => core}/src/idx/planner/plan.rs (100%) rename {lib => core}/src/idx/planner/tree.rs (100%) rename {lib => core}/src/idx/trees/bkeys.rs (100%) rename {lib => core}/src/idx/trees/btree.rs (100%) rename {lib => core}/src/idx/trees/mod.rs (100%) rename {lib => core}/src/idx/trees/mtree.rs (100%) rename {lib => core}/src/idx/trees/store/cache.rs (100%) rename {lib => core}/src/idx/trees/store/mod.rs (100%) rename {lib => core}/src/idx/trees/store/tree.rs (100%) rename {lib => core}/src/idx/trees/vector.rs (100%) rename {lib => core}/src/key/change/mod.rs (100%) rename {lib => core}/src/key/database/all.rs (100%) rename {lib => core}/src/key/database/az.rs (100%) rename {lib => core}/src/key/database/fc.rs (100%) rename {lib => core}/src/key/database/ml.rs (100%) rename {lib => core}/src/key/database/mod.rs (100%) rename {lib => core}/src/key/database/pa.rs (100%) rename {lib => core}/src/key/database/sc.rs (100%) rename {lib => core}/src/key/database/tb.rs (100%) rename {lib => core}/src/key/database/ti.rs (100%) rename {lib => core}/src/key/database/tk.rs (100%) rename {lib => core}/src/key/database/ts.rs (100%) rename {lib => core}/src/key/database/us.rs (100%) rename {lib => core}/src/key/database/vs.rs (100%) rename {lib => core}/src/key/debug.rs (100%) rename {lib => core}/src/key/error.rs (100%) rename {lib => core}/src/key/graph/mod.rs (100%) rename {lib => core}/src/key/index/all.rs (100%) rename {lib => core}/src/key/index/bc.rs (100%) rename {lib => core}/src/key/index/bd.rs (100%) rename {lib => core}/src/key/index/bf.rs (100%) rename {lib => core}/src/key/index/bi.rs (100%) rename {lib => core}/src/key/index/bk.rs (100%) rename {lib => core}/src/key/index/bl.rs (100%) rename {lib => core}/src/key/index/bo.rs (100%) rename {lib => core}/src/key/index/bp.rs (100%) rename {lib => core}/src/key/index/bs.rs (100%) rename {lib => core}/src/key/index/bt.rs (100%) rename {lib => core}/src/key/index/bu.rs (100%) rename {lib => core}/src/key/index/mod.rs (100%) rename {lib => core}/src/key/index/vm.rs (100%) rename {lib => core}/src/key/key_req.rs (100%) rename {lib => core}/src/key/mod.rs (100%) rename {lib => core}/src/key/namespace/all.rs (100%) rename {lib => core}/src/key/namespace/db.rs (100%) rename {lib => core}/src/key/namespace/di.rs (100%) rename {lib => core}/src/key/namespace/mod.rs (100%) rename {lib => core}/src/key/namespace/tk.rs (100%) rename {lib => core}/src/key/namespace/us.rs (100%) rename {lib => core}/src/key/node/all.rs (100%) rename {lib => core}/src/key/node/lq.rs (100%) rename {lib => core}/src/key/node/mod.rs (100%) rename {lib => core}/src/key/root/all.rs (100%) rename {lib => core}/src/key/root/hb.rs (100%) rename {lib => core}/src/key/root/mod.rs (100%) rename {lib => core}/src/key/root/nd.rs (100%) rename {lib => core}/src/key/root/ni.rs (100%) rename {lib => core}/src/key/root/ns.rs (100%) rename {lib => core}/src/key/root/us.rs (100%) rename {lib => core}/src/key/scope/all.rs (100%) rename {lib => core}/src/key/scope/mod.rs (100%) rename {lib => core}/src/key/scope/tk.rs (100%) rename {lib => core}/src/key/table/all.rs (100%) rename {lib => core}/src/key/table/ev.rs (100%) rename {lib => core}/src/key/table/fd.rs (100%) rename {lib => core}/src/key/table/ft.rs (100%) rename {lib => core}/src/key/table/ix.rs (100%) rename {lib => core}/src/key/table/lq.rs (100%) rename {lib => core}/src/key/table/mod.rs (100%) rename {lib => core}/src/key/thing/mod.rs (100%) rename {lib => core}/src/kvs/cache.rs (100%) rename {lib => core}/src/kvs/clock.rs (100%) rename {lib => core}/src/kvs/ds.rs (96%) rename {lib => core}/src/kvs/fdb/mod.rs (100%) rename {lib => core}/src/kvs/indxdb/mod.rs (100%) rename {lib => core}/src/kvs/kv.rs (100%) rename {lib => core}/src/kvs/mem/mod.rs (100%) rename {lib => core}/src/kvs/mod.rs (100%) rename {lib => core}/src/kvs/rocksdb/cnf.rs (100%) rename {lib => core}/src/kvs/rocksdb/mod.rs (100%) rename {lib => core}/src/kvs/speedb/cnf.rs (100%) rename {lib => core}/src/kvs/speedb/mod.rs (100%) rename {lib => core}/src/kvs/tests/cluster_init.rs (100%) rename {lib => core}/src/kvs/tests/hb.rs (100%) rename {lib => core}/src/kvs/tests/helper.rs (100%) rename {lib => core}/src/kvs/tests/lq.rs (100%) rename {lib => core}/src/kvs/tests/mod.rs (100%) rename {lib => core}/src/kvs/tests/multireader.rs (100%) rename {lib => core}/src/kvs/tests/multiwriter_different_keys.rs (100%) rename {lib => core}/src/kvs/tests/multiwriter_same_keys_allow.rs (100%) rename {lib => core}/src/kvs/tests/multiwriter_same_keys_conflict.rs (100%) rename {lib => core}/src/kvs/tests/nd.rs (100%) rename {lib => core}/src/kvs/tests/ndlq.rs (100%) rename {lib => core}/src/kvs/tests/nq.rs (100%) rename {lib => core}/src/kvs/tests/raw.rs (100%) rename {lib => core}/src/kvs/tests/snapshot.rs (100%) rename {lib => core}/src/kvs/tests/tb.rs (100%) rename {lib => core}/src/kvs/tests/tblq.rs (100%) rename {lib => core}/src/kvs/tests/tbnt.rs (100%) rename {lib => core}/src/kvs/tests/timestamp_to_versionstamp.rs (100%) rename {lib => core}/src/kvs/tikv/mod.rs (100%) rename {lib => core}/src/kvs/tx.rs (100%) create mode 100644 core/src/lib.rs rename {lib => core}/src/mac/mod.rs (86%) rename {lib => core}/src/obs/mod.rs (100%) create mode 100644 core/src/sql/mod.rs rename {lib/src/sql => core/src/sql/v1}/algorithm.rs (100%) rename {lib/src/sql => core/src/sql/v1}/arbitrary.rs (100%) rename {lib/src/sql => core/src/sql/v1}/array.rs (100%) rename {lib/src/sql => core/src/sql/v1}/base.rs (100%) rename {lib/src/sql => core/src/sql/v1}/block.rs (100%) rename {lib/src/sql => core/src/sql/v1}/bytes.rs (100%) rename {lib/src/sql => core/src/sql/v1}/cast.rs (100%) rename {lib/src/sql => core/src/sql/v1}/changefeed.rs (100%) rename {lib/src/sql => core/src/sql/v1}/cond.rs (100%) rename {lib/src/sql => core/src/sql/v1}/constant.rs (100%) rename {lib/src/sql => core/src/sql/v1}/data.rs (100%) rename {lib/src/sql => core/src/sql/v1}/datetime.rs (100%) rename {lib/src/sql => core/src/sql/v1}/dir.rs (100%) rename {lib/src/sql => core/src/sql/v1}/duration.rs (100%) rename {lib/src/sql => core/src/sql/v1}/edges.rs (100%) rename {lib/src/sql => core/src/sql/v1}/ending.rs (100%) rename {lib/src/sql => core/src/sql/v1}/escape.rs (100%) rename {lib/src/sql => core/src/sql/v1}/explain.rs (100%) rename {lib/src/sql => core/src/sql/v1}/expression.rs (98%) rename {lib/src/sql => core/src/sql/v1}/fetch.rs (100%) rename {lib/src/sql => core/src/sql/v1}/field.rs (100%) rename {lib/src/sql => core/src/sql/v1}/filter.rs (100%) rename {lib/src/sql => core/src/sql/v1}/fmt.rs (100%) rename {lib/src/sql => core/src/sql/v1}/function.rs (100%) rename {lib/src/sql => core/src/sql/v1}/future.rs (100%) rename {lib/src/sql => core/src/sql/v1}/geometry.rs (100%) rename {lib/src/sql => core/src/sql/v1}/graph.rs (100%) rename {lib/src/sql => core/src/sql/v1}/group.rs (100%) rename {lib/src/sql => core/src/sql/v1}/id.rs (100%) rename {lib/src/sql => core/src/sql/v1}/ident.rs (100%) rename {lib/src/sql => core/src/sql/v1}/idiom.rs (100%) rename {lib/src/sql => core/src/sql/v1}/index.rs (100%) rename {lib/src/sql => core/src/sql/v1}/kind.rs (100%) rename {lib/src/sql => core/src/sql/v1}/language.rs (100%) rename {lib/src/sql => core/src/sql/v1}/limit.rs (100%) rename {lib/src/sql => core/src/sql/v1}/mock.rs (100%) rename {lib/src/sql => core/src/sql/v1}/mod.rs (96%) rename {lib/src/sql => core/src/sql/v1}/model.rs (100%) rename {lib/src/sql => core/src/sql/v1}/number.rs (100%) rename {lib/src/sql => core/src/sql/v1}/object.rs (100%) rename {lib/src/sql => core/src/sql/v1}/operation.rs (100%) rename {lib/src/sql => core/src/sql/v1}/operator.rs (100%) rename {lib/src/sql => core/src/sql/v1}/order.rs (100%) rename {lib/src/sql => core/src/sql/v1}/output.rs (100%) rename {lib/src/sql => core/src/sql/v1}/param.rs (100%) rename {lib/src/sql => core/src/sql/v1}/part.rs (100%) rename {lib/src/sql => core/src/sql/v1}/paths.rs (100%) rename {lib/src/sql => core/src/sql/v1}/permission.rs (100%) rename {lib/src/sql => core/src/sql/v1}/query.rs (100%) rename {lib/src/sql => core/src/sql/v1}/range.rs (100%) rename {lib/src/sql => core/src/sql/v1}/regex.rs (100%) rename {lib/src/sql => core/src/sql/v1}/scoring.rs (100%) rename {lib/src/sql => core/src/sql/v1}/script.rs (100%) rename {lib/src/sql => core/src/sql/v1}/serde.rs (100%) rename {lib/src/sql => core/src/sql/v1}/split.rs (100%) rename {lib/src/sql => core/src/sql/v1}/start.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statement.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/analyze.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/begin.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/break.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/cancel.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/commit.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/continue.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/create.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/define/analyzer.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/define/database.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/define/event.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/define/field.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/define/function.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/define/index.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/define/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/define/model.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/define/namespace.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/define/param.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/define/scope.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/define/table.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/define/token.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/define/user.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/delete.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/foreach.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/ifelse.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/info.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/insert.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/kill.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/live.rs (95%) rename {lib/src/sql => core/src/sql/v1}/statements/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/option.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/output.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/relate.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/remove/analyzer.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/remove/database.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/remove/event.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/remove/field.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/remove/function.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/remove/index.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/remove/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/remove/model.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/remove/namespace.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/remove/param.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/remove/scope.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/remove/table.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/remove/token.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/remove/user.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/select.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/set.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/show.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/sleep.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/throw.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/update.rs (100%) rename {lib/src/sql => core/src/sql/v1}/statements/use.rs (100%) rename {lib/src/sql => core/src/sql/v1}/strand.rs (100%) rename {lib/src/sql => core/src/sql/v1}/subquery.rs (100%) rename {lib/src/sql => core/src/sql/v1}/table.rs (100%) rename {lib/src/sql => core/src/sql/v1}/thing.rs (100%) rename {lib/src/sql => core/src/sql/v1}/timeout.rs (100%) rename {lib/src/sql => core/src/sql/v1}/tokenizer.rs (100%) rename {lib/src/sql => core/src/sql/v1}/uuid.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/all.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/changed.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/clear.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/compare.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/cut.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/dec.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/decrement.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/def.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/del.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/diff.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/each.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/every.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/extend.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/fetch.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/first.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/flatten.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/generate.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/get.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/inc.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/increment.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/last.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/merge.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/patch.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/pick.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/put.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/replace.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/rid.rs (100%) create mode 100644 core/src/sql/v1/value/serde/de/mod.rs create mode 100644 core/src/sql/v1/value/serde/mod.rs rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/algorithm/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/base/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/base/opt.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/block/entry/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/block/entry/vec.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/block/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/cast/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/changefeed/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/changefeed/opt.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/cond/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/cond/opt.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/constant/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/data/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/data/opt.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/datetime/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/decimal/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/dir/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/distance/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/duration/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/duration/opt.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/edges/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/explain/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/explain/opt.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/expression/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/fetch/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/fetch/vec/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/fetch/vec/opt.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/fetchs/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/fetchs/opt.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/field/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/field/vec.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/fields/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/filter/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/filter/vec/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/filter/vec/opt.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/function/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/geometry/coord/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/geometry/coord/vec.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/geometry/line_string/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/geometry/line_string/vec.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/geometry/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/geometry/point/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/geometry/point/vec.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/geometry/polygon/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/geometry/polygon/vec.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/geometry/vec.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/graph/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/group/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/group/vec/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/group/vec/opt.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/id/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/ident/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/ident/vec.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/idiom/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/idiom/vec/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/idiom/vec/opt.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/index/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/index/mtreeparams.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/index/searchparams.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/kind/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/kind/opt.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/kind/vec.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/language/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/limit/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/limit/opt.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/mock/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/number/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/operator/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/order/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/order/vec/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/order/vec/opt.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/output/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/output/opt.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/part/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/part/vec/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/part/vec/opt.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/permission/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/permissions/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/primitive/bool.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/primitive/f32.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/primitive/f64.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/primitive/i64.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/primitive/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/primitive/opt/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/primitive/opt/u32.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/primitive/opt/u64.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/primitive/u16.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/primitive/u32.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/primitive/u64.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/primitive/u8.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/range/bound.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/range/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/scoring/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/split/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/split/vec/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/split/vec/opt.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/start/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/start/opt.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/analyze.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/begin.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/break.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/cancel.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/commit.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/continue.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/create.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/define/analyzer.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/define/database.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/define/event.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/define/field.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/define/function.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/define/index.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/define/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/define/namespace.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/define/param.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/define/scope.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/define/table.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/define/token.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/define/user.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/delete.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/ifelse.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/info.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/insert.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/kill.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/live.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/option.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/output.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/relate.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/remove/analyzer.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/remove/database.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/remove/event.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/remove/field.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/remove/function.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/remove/index.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/remove/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/remove/namespace.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/remove/param.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/remove/scope.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/remove/table.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/remove/token.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/remove/user.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/select.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/set.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/show/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/show/since.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/sleep.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/throw.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/update.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/vec.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/statement/yuse.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/strand/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/strand/opt.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/string/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/string/opt.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/string/vec.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/subquery/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/table/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/table/opt.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/table/vec.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/thing/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/timeout/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/timeout/opt.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/tokenizer/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/tokenizer/vec/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/tokenizer/vec/opt.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/uuid/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/uuid/opt.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/value/map.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/value/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/value/opt.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/value/vec.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/vectortype/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/version/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/version/opt.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/view/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/view/opt.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/with/mod.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/serde/ser/with/opt.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/set.rs (100%) rename {lib/src/sql => core/src/sql/v1}/value/value.rs (99%) rename {lib/src/sql => core/src/sql/v1}/value/walk.rs (100%) rename {lib/src/sql => core/src/sql/v1}/version.rs (100%) rename {lib/src/sql => core/src/sql/v1}/view.rs (100%) rename {lib/src/sql => core/src/sql/v1}/with.rs (100%) create mode 100644 core/src/sql/v2/algorithm.rs create mode 100644 core/src/sql/v2/arbitrary.rs create mode 100644 core/src/sql/v2/array.rs create mode 100644 core/src/sql/v2/base.rs create mode 100644 core/src/sql/v2/block.rs create mode 100644 core/src/sql/v2/bytes.rs create mode 100644 core/src/sql/v2/cast.rs create mode 100644 core/src/sql/v2/changefeed.rs create mode 100644 core/src/sql/v2/cond.rs create mode 100644 core/src/sql/v2/constant.rs create mode 100644 core/src/sql/v2/data.rs create mode 100644 core/src/sql/v2/datetime.rs create mode 100644 core/src/sql/v2/dir.rs create mode 100644 core/src/sql/v2/duration.rs create mode 100644 core/src/sql/v2/edges.rs create mode 100644 core/src/sql/v2/ending.rs create mode 100644 core/src/sql/v2/escape.rs create mode 100644 core/src/sql/v2/explain.rs create mode 100644 core/src/sql/v2/expression.rs create mode 100644 core/src/sql/v2/fetch.rs create mode 100644 core/src/sql/v2/field.rs create mode 100644 core/src/sql/v2/filter.rs create mode 100644 core/src/sql/v2/fmt.rs create mode 100644 core/src/sql/v2/function.rs create mode 100644 core/src/sql/v2/future.rs create mode 100644 core/src/sql/v2/geometry.rs create mode 100644 core/src/sql/v2/graph.rs create mode 100644 core/src/sql/v2/group.rs create mode 100644 core/src/sql/v2/id.rs create mode 100644 core/src/sql/v2/ident.rs create mode 100644 core/src/sql/v2/idiom.rs create mode 100644 core/src/sql/v2/index.rs create mode 100644 core/src/sql/v2/kind.rs create mode 100644 core/src/sql/v2/language.rs create mode 100644 core/src/sql/v2/limit.rs create mode 100644 core/src/sql/v2/mock.rs create mode 100644 core/src/sql/v2/mod.rs create mode 100644 core/src/sql/v2/model.rs create mode 100644 core/src/sql/v2/number.rs create mode 100644 core/src/sql/v2/object.rs create mode 100644 core/src/sql/v2/operation.rs create mode 100644 core/src/sql/v2/operator.rs create mode 100644 core/src/sql/v2/order.rs create mode 100644 core/src/sql/v2/output.rs create mode 100644 core/src/sql/v2/param.rs create mode 100644 core/src/sql/v2/part.rs create mode 100644 core/src/sql/v2/paths.rs create mode 100644 core/src/sql/v2/permission.rs create mode 100644 core/src/sql/v2/query.rs create mode 100644 core/src/sql/v2/range.rs create mode 100644 core/src/sql/v2/regex.rs create mode 100644 core/src/sql/v2/scoring.rs create mode 100644 core/src/sql/v2/script.rs create mode 100644 core/src/sql/v2/serde.rs create mode 100644 core/src/sql/v2/split.rs create mode 100644 core/src/sql/v2/start.rs create mode 100644 core/src/sql/v2/statement.rs create mode 100644 core/src/sql/v2/statements/analyze.rs create mode 100644 core/src/sql/v2/statements/begin.rs create mode 100644 core/src/sql/v2/statements/break.rs create mode 100644 core/src/sql/v2/statements/cancel.rs create mode 100644 core/src/sql/v2/statements/commit.rs create mode 100644 core/src/sql/v2/statements/continue.rs create mode 100644 core/src/sql/v2/statements/create.rs create mode 100644 core/src/sql/v2/statements/define/analyzer.rs create mode 100644 core/src/sql/v2/statements/define/database.rs create mode 100644 core/src/sql/v2/statements/define/event.rs create mode 100644 core/src/sql/v2/statements/define/field.rs create mode 100644 core/src/sql/v2/statements/define/function.rs create mode 100644 core/src/sql/v2/statements/define/index.rs create mode 100644 core/src/sql/v2/statements/define/mod.rs create mode 100644 core/src/sql/v2/statements/define/model.rs create mode 100644 core/src/sql/v2/statements/define/namespace.rs create mode 100644 core/src/sql/v2/statements/define/param.rs create mode 100644 core/src/sql/v2/statements/define/scope.rs create mode 100644 core/src/sql/v2/statements/define/table.rs create mode 100644 core/src/sql/v2/statements/define/token.rs create mode 100644 core/src/sql/v2/statements/define/user.rs create mode 100644 core/src/sql/v2/statements/delete.rs create mode 100644 core/src/sql/v2/statements/foreach.rs create mode 100644 core/src/sql/v2/statements/ifelse.rs create mode 100644 core/src/sql/v2/statements/info.rs create mode 100644 core/src/sql/v2/statements/insert.rs create mode 100644 core/src/sql/v2/statements/kill.rs create mode 100644 core/src/sql/v2/statements/live.rs create mode 100644 core/src/sql/v2/statements/mod.rs create mode 100644 core/src/sql/v2/statements/option.rs create mode 100644 core/src/sql/v2/statements/output.rs create mode 100644 core/src/sql/v2/statements/relate.rs create mode 100644 core/src/sql/v2/statements/remove/analyzer.rs create mode 100644 core/src/sql/v2/statements/remove/database.rs create mode 100644 core/src/sql/v2/statements/remove/event.rs create mode 100644 core/src/sql/v2/statements/remove/field.rs create mode 100644 core/src/sql/v2/statements/remove/function.rs create mode 100644 core/src/sql/v2/statements/remove/index.rs create mode 100644 core/src/sql/v2/statements/remove/mod.rs create mode 100644 core/src/sql/v2/statements/remove/model.rs create mode 100644 core/src/sql/v2/statements/remove/namespace.rs create mode 100644 core/src/sql/v2/statements/remove/param.rs create mode 100644 core/src/sql/v2/statements/remove/scope.rs create mode 100644 core/src/sql/v2/statements/remove/table.rs create mode 100644 core/src/sql/v2/statements/remove/token.rs create mode 100644 core/src/sql/v2/statements/remove/user.rs create mode 100644 core/src/sql/v2/statements/select.rs create mode 100644 core/src/sql/v2/statements/set.rs create mode 100644 core/src/sql/v2/statements/show.rs create mode 100644 core/src/sql/v2/statements/sleep.rs create mode 100644 core/src/sql/v2/statements/throw.rs create mode 100644 core/src/sql/v2/statements/update.rs create mode 100644 core/src/sql/v2/statements/use.rs create mode 100644 core/src/sql/v2/strand.rs create mode 100644 core/src/sql/v2/subquery.rs create mode 100644 core/src/sql/v2/table.rs create mode 100644 core/src/sql/v2/thing.rs create mode 100644 core/src/sql/v2/timeout.rs create mode 100644 core/src/sql/v2/tokenizer.rs create mode 100644 core/src/sql/v2/uuid.rs create mode 100644 core/src/sql/v2/value/all.rs create mode 100644 core/src/sql/v2/value/changed.rs create mode 100644 core/src/sql/v2/value/clear.rs create mode 100644 core/src/sql/v2/value/compare.rs create mode 100644 core/src/sql/v2/value/cut.rs create mode 100644 core/src/sql/v2/value/dec.rs create mode 100644 core/src/sql/v2/value/decrement.rs create mode 100644 core/src/sql/v2/value/def.rs create mode 100644 core/src/sql/v2/value/del.rs create mode 100644 core/src/sql/v2/value/diff.rs create mode 100644 core/src/sql/v2/value/each.rs create mode 100644 core/src/sql/v2/value/every.rs create mode 100644 core/src/sql/v2/value/extend.rs create mode 100644 core/src/sql/v2/value/fetch.rs create mode 100644 core/src/sql/v2/value/first.rs create mode 100644 core/src/sql/v2/value/flatten.rs create mode 100644 core/src/sql/v2/value/generate.rs create mode 100644 core/src/sql/v2/value/get.rs create mode 100644 core/src/sql/v2/value/inc.rs create mode 100644 core/src/sql/v2/value/increment.rs create mode 100644 core/src/sql/v2/value/last.rs create mode 100644 core/src/sql/v2/value/merge.rs create mode 100644 core/src/sql/v2/value/mod.rs create mode 100644 core/src/sql/v2/value/patch.rs create mode 100644 core/src/sql/v2/value/pick.rs create mode 100644 core/src/sql/v2/value/put.rs create mode 100644 core/src/sql/v2/value/replace.rs create mode 100644 core/src/sql/v2/value/rid.rs create mode 100644 core/src/sql/v2/value/serde/de/mod.rs create mode 100644 core/src/sql/v2/value/serde/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/algorithm/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/base/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/base/opt.rs create mode 100644 core/src/sql/v2/value/serde/ser/block/entry/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/block/entry/vec.rs create mode 100644 core/src/sql/v2/value/serde/ser/block/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/cast/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/changefeed/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/changefeed/opt.rs create mode 100644 core/src/sql/v2/value/serde/ser/cond/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/cond/opt.rs create mode 100644 core/src/sql/v2/value/serde/ser/constant/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/data/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/data/opt.rs create mode 100644 core/src/sql/v2/value/serde/ser/datetime/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/decimal/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/dir/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/distance/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/duration/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/duration/opt.rs create mode 100644 core/src/sql/v2/value/serde/ser/edges/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/explain/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/explain/opt.rs create mode 100644 core/src/sql/v2/value/serde/ser/expression/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/fetch/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/fetch/vec/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/fetch/vec/opt.rs create mode 100644 core/src/sql/v2/value/serde/ser/fetchs/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/fetchs/opt.rs create mode 100644 core/src/sql/v2/value/serde/ser/field/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/field/vec.rs create mode 100644 core/src/sql/v2/value/serde/ser/fields/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/filter/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/filter/vec/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/filter/vec/opt.rs create mode 100644 core/src/sql/v2/value/serde/ser/function/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/geometry/coord/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/geometry/coord/vec.rs create mode 100644 core/src/sql/v2/value/serde/ser/geometry/line_string/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/geometry/line_string/vec.rs create mode 100644 core/src/sql/v2/value/serde/ser/geometry/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/geometry/point/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/geometry/point/vec.rs create mode 100644 core/src/sql/v2/value/serde/ser/geometry/polygon/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/geometry/polygon/vec.rs create mode 100644 core/src/sql/v2/value/serde/ser/geometry/vec.rs create mode 100644 core/src/sql/v2/value/serde/ser/graph/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/group/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/group/vec/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/group/vec/opt.rs create mode 100644 core/src/sql/v2/value/serde/ser/id/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/ident/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/ident/vec.rs create mode 100644 core/src/sql/v2/value/serde/ser/idiom/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/idiom/vec/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/idiom/vec/opt.rs create mode 100644 core/src/sql/v2/value/serde/ser/index/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/index/mtreeparams.rs create mode 100644 core/src/sql/v2/value/serde/ser/index/searchparams.rs create mode 100644 core/src/sql/v2/value/serde/ser/kind/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/kind/opt.rs create mode 100644 core/src/sql/v2/value/serde/ser/kind/vec.rs create mode 100644 core/src/sql/v2/value/serde/ser/language/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/limit/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/limit/opt.rs create mode 100644 core/src/sql/v2/value/serde/ser/mock/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/number/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/operator/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/order/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/order/vec/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/order/vec/opt.rs create mode 100644 core/src/sql/v2/value/serde/ser/output/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/output/opt.rs create mode 100644 core/src/sql/v2/value/serde/ser/part/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/part/vec/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/part/vec/opt.rs create mode 100644 core/src/sql/v2/value/serde/ser/permission/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/permissions/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/primitive/bool.rs create mode 100644 core/src/sql/v2/value/serde/ser/primitive/f32.rs create mode 100644 core/src/sql/v2/value/serde/ser/primitive/f64.rs create mode 100644 core/src/sql/v2/value/serde/ser/primitive/i64.rs create mode 100644 core/src/sql/v2/value/serde/ser/primitive/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/primitive/opt/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/primitive/opt/u32.rs create mode 100644 core/src/sql/v2/value/serde/ser/primitive/opt/u64.rs create mode 100644 core/src/sql/v2/value/serde/ser/primitive/u16.rs create mode 100644 core/src/sql/v2/value/serde/ser/primitive/u32.rs create mode 100644 core/src/sql/v2/value/serde/ser/primitive/u64.rs create mode 100644 core/src/sql/v2/value/serde/ser/primitive/u8.rs create mode 100644 core/src/sql/v2/value/serde/ser/range/bound.rs create mode 100644 core/src/sql/v2/value/serde/ser/range/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/scoring/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/split/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/split/vec/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/split/vec/opt.rs create mode 100644 core/src/sql/v2/value/serde/ser/start/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/start/opt.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/analyze.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/begin.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/break.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/cancel.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/commit.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/continue.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/create.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/define/analyzer.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/define/database.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/define/event.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/define/field.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/define/function.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/define/index.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/define/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/define/namespace.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/define/param.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/define/scope.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/define/table.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/define/token.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/define/user.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/delete.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/ifelse.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/info.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/insert.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/kill.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/live.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/option.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/output.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/relate.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/remove/analyzer.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/remove/database.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/remove/event.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/remove/field.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/remove/function.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/remove/index.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/remove/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/remove/namespace.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/remove/param.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/remove/scope.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/remove/table.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/remove/token.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/remove/user.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/select.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/set.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/show/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/show/since.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/sleep.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/throw.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/update.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/vec.rs create mode 100644 core/src/sql/v2/value/serde/ser/statement/yuse.rs create mode 100644 core/src/sql/v2/value/serde/ser/strand/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/strand/opt.rs create mode 100644 core/src/sql/v2/value/serde/ser/string/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/string/opt.rs create mode 100644 core/src/sql/v2/value/serde/ser/string/vec.rs create mode 100644 core/src/sql/v2/value/serde/ser/subquery/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/table/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/table/opt.rs create mode 100644 core/src/sql/v2/value/serde/ser/table/vec.rs create mode 100644 core/src/sql/v2/value/serde/ser/thing/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/timeout/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/timeout/opt.rs create mode 100644 core/src/sql/v2/value/serde/ser/tokenizer/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/tokenizer/vec/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/tokenizer/vec/opt.rs create mode 100644 core/src/sql/v2/value/serde/ser/uuid/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/uuid/opt.rs create mode 100644 core/src/sql/v2/value/serde/ser/value/map.rs create mode 100644 core/src/sql/v2/value/serde/ser/value/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/value/opt.rs create mode 100644 core/src/sql/v2/value/serde/ser/value/vec.rs create mode 100644 core/src/sql/v2/value/serde/ser/vectortype/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/version/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/version/opt.rs create mode 100644 core/src/sql/v2/value/serde/ser/view/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/view/opt.rs create mode 100644 core/src/sql/v2/value/serde/ser/with/mod.rs create mode 100644 core/src/sql/v2/value/serde/ser/with/opt.rs create mode 100644 core/src/sql/v2/value/set.rs create mode 100644 core/src/sql/v2/value/value.rs create mode 100644 core/src/sql/v2/value/walk.rs create mode 100644 core/src/sql/v2/version.rs create mode 100644 core/src/sql/v2/view.rs create mode 100644 core/src/sql/v2/with.rs rename {lib => core}/src/syn/common.rs (100%) rename {lib => core}/src/syn/error/mod.rs (100%) rename {lib => core}/src/syn/error/nom_error.rs (100%) rename {lib => core}/src/syn/mod.rs (100%) rename {lib => core}/src/syn/v1/block.rs (100%) rename {lib => core}/src/syn/v1/builtin.rs (100%) rename {lib => core}/src/syn/v1/comment.rs (100%) rename {lib => core}/src/syn/v1/common.rs (100%) rename {lib => core}/src/syn/v1/depth.rs (100%) rename {lib => core}/src/syn/v1/ending.rs (100%) rename {lib => core}/src/syn/v1/error.rs (100%) rename {lib => core}/src/syn/v1/expression.rs (100%) rename {lib => core}/src/syn/v1/function.rs (100%) rename {lib => core}/src/syn/v1/idiom.rs (100%) rename {lib => core}/src/syn/v1/kind.rs (100%) rename {lib => core}/src/syn/v1/literal/algorithm.rs (100%) rename {lib => core}/src/syn/v1/literal/datetime.rs (100%) rename {lib => core}/src/syn/v1/literal/duration.rs (100%) rename {lib => core}/src/syn/v1/literal/filter.rs (100%) rename {lib => core}/src/syn/v1/literal/language.rs (100%) rename {lib => core}/src/syn/v1/literal/mod.rs (100%) rename {lib => core}/src/syn/v1/literal/number.rs (100%) rename {lib => core}/src/syn/v1/literal/range.rs (100%) rename {lib => core}/src/syn/v1/literal/regex.rs (100%) rename {lib => core}/src/syn/v1/literal/scoring.rs (100%) rename {lib => core}/src/syn/v1/literal/strand.rs (100%) rename {lib => core}/src/syn/v1/literal/tokenizer.rs (100%) rename {lib => core}/src/syn/v1/literal/uuid.rs (100%) rename {lib => core}/src/syn/v1/mod.rs (100%) rename {lib => core}/src/syn/v1/omit.rs (100%) rename {lib => core}/src/syn/v1/operator.rs (100%) rename {lib => core}/src/syn/v1/part/data.rs (100%) rename {lib => core}/src/syn/v1/part/field.rs (100%) rename {lib => core}/src/syn/v1/part/index.rs (100%) rename {lib => core}/src/syn/v1/part/mod.rs (100%) rename {lib => core}/src/syn/v1/part/permission.rs (100%) rename {lib => core}/src/syn/v1/part/split.rs (100%) rename {lib => core}/src/syn/v1/part/start.rs (100%) rename {lib => core}/src/syn/v1/part/timeout.rs (100%) rename {lib => core}/src/syn/v1/part/view.rs (100%) rename {lib => core}/src/syn/v1/part/with.rs (100%) rename {lib => core}/src/syn/v1/special.rs (100%) rename {lib => core}/src/syn/v1/stmt/analyze.rs (100%) rename {lib => core}/src/syn/v1/stmt/begin.rs (100%) rename {lib => core}/src/syn/v1/stmt/cancel.rs (100%) rename {lib => core}/src/syn/v1/stmt/commit.rs (100%) rename {lib => core}/src/syn/v1/stmt/create.rs (100%) rename {lib => core}/src/syn/v1/stmt/define/analyzer.rs (100%) rename {lib => core}/src/syn/v1/stmt/define/database.rs (100%) rename {lib => core}/src/syn/v1/stmt/define/event.rs (100%) rename {lib => core}/src/syn/v1/stmt/define/field.rs (100%) rename {lib => core}/src/syn/v1/stmt/define/function.rs (100%) rename {lib => core}/src/syn/v1/stmt/define/index.rs (100%) rename {lib => core}/src/syn/v1/stmt/define/mod.rs (100%) rename {lib => core}/src/syn/v1/stmt/define/namespace.rs (100%) rename {lib => core}/src/syn/v1/stmt/define/param.rs (100%) rename {lib => core}/src/syn/v1/stmt/define/scope.rs (100%) rename {lib => core}/src/syn/v1/stmt/define/table.rs (100%) rename {lib => core}/src/syn/v1/stmt/define/token.rs (100%) rename {lib => core}/src/syn/v1/stmt/define/user.rs (100%) rename {lib => core}/src/syn/v1/stmt/delete.rs (100%) rename {lib => core}/src/syn/v1/stmt/flow.rs (100%) rename {lib => core}/src/syn/v1/stmt/foreach.rs (100%) rename {lib => core}/src/syn/v1/stmt/ifelse.rs (100%) rename {lib => core}/src/syn/v1/stmt/info.rs (100%) rename {lib => core}/src/syn/v1/stmt/insert.rs (100%) rename {lib => core}/src/syn/v1/stmt/kill.rs (100%) rename {lib => core}/src/syn/v1/stmt/live.rs (100%) rename {lib => core}/src/syn/v1/stmt/mod.rs (100%) rename {lib => core}/src/syn/v1/stmt/option.rs (100%) rename {lib => core}/src/syn/v1/stmt/output.rs (100%) rename {lib => core}/src/syn/v1/stmt/relate.rs (100%) rename {lib => core}/src/syn/v1/stmt/remove.rs (100%) rename {lib => core}/src/syn/v1/stmt/select.rs (100%) rename {lib => core}/src/syn/v1/stmt/set.rs (100%) rename {lib => core}/src/syn/v1/stmt/show.rs (100%) rename {lib => core}/src/syn/v1/stmt/sleep.rs (100%) rename {lib => core}/src/syn/v1/stmt/throw.rs (100%) rename {lib => core}/src/syn/v1/stmt/update.rs (100%) rename {lib => core}/src/syn/v1/stmt/use.rs (100%) rename {lib => core}/src/syn/v1/subquery.rs (100%) rename {lib => core}/src/syn/v1/test.rs (100%) rename {lib => core}/src/syn/v1/thing.rs (100%) rename {lib => core}/src/syn/v1/value/geometry.rs (100%) rename {lib => core}/src/syn/v1/value/mock.rs (100%) rename {lib => core}/src/syn/v1/value/mod.rs (100%) rename {lib => core}/src/syn/v2/lexer/byte.rs (100%) rename {lib => core}/src/syn/v2/lexer/char.rs (100%) rename {lib => core}/src/syn/v2/lexer/datetime.rs (100%) rename {lib => core}/src/syn/v2/lexer/duration.rs (100%) rename {lib => core}/src/syn/v2/lexer/ident.rs (100%) rename {lib => core}/src/syn/v2/lexer/js.rs (100%) rename {lib => core}/src/syn/v2/lexer/keywords.rs (100%) rename {lib => core}/src/syn/v2/lexer/mod.rs (100%) rename {lib => core}/src/syn/v2/lexer/number.rs (100%) rename {lib => core}/src/syn/v2/lexer/reader.rs (100%) rename {lib => core}/src/syn/v2/lexer/strand.rs (100%) rename {lib => core}/src/syn/v2/lexer/test.rs (100%) rename {lib => core}/src/syn/v2/lexer/unicode.rs (100%) rename {lib => core}/src/syn/v2/lexer/uuid.rs (100%) rename {lib => core}/src/syn/v2/mod.rs (100%) rename {lib => core}/src/syn/v2/parser/basic.rs (100%) rename {lib => core}/src/syn/v2/parser/builtin.rs (100%) rename {lib => core}/src/syn/v2/parser/error.rs (100%) rename {lib => core}/src/syn/v2/parser/expression.rs (100%) rename {lib => core}/src/syn/v2/parser/function.rs (100%) rename {lib => core}/src/syn/v2/parser/idiom.rs (100%) rename {lib => core}/src/syn/v2/parser/json.rs (100%) rename {lib => core}/src/syn/v2/parser/kind.rs (100%) rename {lib => core}/src/syn/v2/parser/mac.rs (100%) rename {lib => core}/src/syn/v2/parser/mod.rs (100%) rename {lib => core}/src/syn/v2/parser/object.rs (100%) rename {lib => core}/src/syn/v2/parser/prime.rs (100%) rename {lib => core}/src/syn/v2/parser/stmt/create.rs (100%) rename {lib => core}/src/syn/v2/parser/stmt/define.rs (100%) rename {lib => core}/src/syn/v2/parser/stmt/delete.rs (100%) rename {lib => core}/src/syn/v2/parser/stmt/if.rs (100%) rename {lib => core}/src/syn/v2/parser/stmt/insert.rs (100%) rename {lib => core}/src/syn/v2/parser/stmt/mod.rs (100%) rename {lib => core}/src/syn/v2/parser/stmt/parts.rs (100%) rename {lib => core}/src/syn/v2/parser/stmt/relate.rs (100%) rename {lib => core}/src/syn/v2/parser/stmt/remove.rs (100%) rename {lib => core}/src/syn/v2/parser/stmt/select.rs (100%) rename {lib => core}/src/syn/v2/parser/stmt/update.rs (100%) rename {lib => core}/src/syn/v2/parser/test/mod.rs (100%) rename {lib => core}/src/syn/v2/parser/test/stmt.rs (100%) rename {lib => core}/src/syn/v2/parser/test/streaming.rs (100%) rename {lib => core}/src/syn/v2/parser/test/value.rs (100%) rename {lib => core}/src/syn/v2/parser/thing.rs (100%) rename {lib => core}/src/syn/v2/parser/token_buffer.rs (100%) rename {lib => core}/src/syn/v2/test.rs (100%) rename {lib => core}/src/syn/v2/token/keyword.rs (100%) rename {lib => core}/src/syn/v2/token/mac.rs (100%) rename {lib => core}/src/syn/v2/token/mod.rs (100%) rename {lib => core}/src/vs/conv.rs (100%) rename {lib => core}/src/vs/mod.rs (100%) rename {lib => core}/src/vs/oracle.rs (100%) rename {lib => core}/test.surql (100%) delete mode 100644 lib/src/sql/value/serde/mod.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d4a17fd4..825567cc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -383,6 +383,8 @@ jobs: run: cargo install --debug --locked cargo-make - name: Test workspace for experimental_parser + env: + RUSTFLAGS: "--cfg surrealdb_unstable" run: cargo make test-experimental-parser ws-engine: diff --git a/Cargo.lock b/Cargo.lock index 647eb1ff..b9199098 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -652,17 +652,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "axum-client-ip" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f5ffe4637708b326c621d5494ab6c91dcf62ee440fa6ee967d289315a9c6f81" -dependencies = [ - "axum 0.7.4", - "forwarded-header-value", - "serde", -] - [[package]] name = "axum-core" version = "0.3.4" @@ -1917,16 +1906,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "forwarded-header-value" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835f84f38484cc86f110a805655697908257fb9a7af005234060891557198e9" -dependencies = [ - "nonempty", - "thiserror", -] - [[package]] name = "foundationdb" version = "0.8.0" @@ -3278,12 +3257,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "nonempty" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7" - [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -5283,7 +5256,6 @@ dependencies = [ "argon2", "assert_fs", "axum 0.6.20", - "axum-client-ip", "axum-extra", "axum-server", "base64 0.21.7", @@ -5297,10 +5269,8 @@ dependencies = [ "http 0.2.11", "http-body 0.4.6", "hyper 0.14.28", - "ipnet", "jemallocator", "mimalloc", - "ndarray", "nix 0.27.1", "once_cell", "opentelemetry", @@ -5318,7 +5288,6 @@ dependencies = [ "serde_json", "serial_test", "surrealdb", - "surrealml-core", "temp-env", "tempfile", "test-log", @@ -5342,6 +5311,56 @@ dependencies = [ [[package]] name = "surrealdb" version = "1.1.1" +dependencies = [ + "async-channel", + "bincode", + "chrono", + "criterion", + "dmp", + "env_logger", + "flume", + "futures", + "futures-concurrency", + "geo 0.27.0", + "indexmap 2.1.0", + "native-tls", + "once_cell", + "path-clean", + "pharos", + "pprof", + "rand 0.8.5", + "regex", + "reqwest", + "ring 0.17.7", + "rust_decimal", + "rustls", + "semver", + "serde", + "serde_json", + "serial_test", + "surrealdb-core", + "temp-dir", + "test-log", + "thiserror", + "time", + "tokio", + "tokio-tungstenite", + "tokio-util", + "tracing", + "tracing-subscriber", + "trice", + "ulid", + "url", + "uuid", + "wasm-bindgen-futures", + "wasmtimer", + "wiremock", + "ws_stream_wasm", +] + +[[package]] +name = "surrealdb-core" +version = "1.1.1" dependencies = [ "addr", "any_ascii", @@ -5361,28 +5380,23 @@ dependencies = [ "dmp", "echodb", "env_logger", - "flume", "foundationdb", "fst", "futures", - "futures-concurrency", "fuzzy-matcher", "geo 0.27.0", "geo-types", "hex", - "indexmap 2.1.0", "indxdb", "ipnet", "lexicmp", "md-5", "nanoid", - "native-tls", "ndarray", "nom", "num_cpus", "object_store", "once_cell", - "path-clean", "pbkdf2", "pharos", "phf", @@ -5401,7 +5415,6 @@ dependencies = [ "rquickjs", "rust-stemmers", "rust_decimal", - "rustls", "scrypt", "semver", "serde", @@ -5422,7 +5435,6 @@ dependencies = [ "time", "tokio", "tokio-tungstenite", - "tokio-util", "tracing", "tracing-subscriber", "trice", diff --git a/Cargo.toml b/Cargo.toml index d4018a27..de3c7346 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,12 +17,13 @@ storage-fdb = ["surrealdb/kv-fdb-7_1"] scripting = ["surrealdb/scripting"] http = ["surrealdb/http"] http-compression = [] -ml = ["surrealdb/ml", "surrealml-core"] -experimental-parser = ["surrealdb/experimental-parser"] +ml = ["surrealdb/ml"] jwks = ["surrealdb/jwks"] +sql2 = ["surrealdb/sql2"] +parser2 = ["surrealdb/parser2"] [workspace] -members = ["lib", "lib/examples/actix", "lib/examples/axum"] +members = ["core", "lib", "lib/examples/actix", "lib/examples/axum"] [profile.release] lto = true @@ -37,7 +38,6 @@ strip = false [dependencies] argon2 = "0.5.2" axum = { version = "0.6.20", features = ["tracing", "ws", "headers"] } -axum-client-ip = "0.5.0" axum-extra = { version = "0.7.7", features = ["query", "typed-routing"] } axum-server = { version = "0.5.1", features = ["tls-rustls"] } base64 = "0.21.5" @@ -55,8 +55,6 @@ glob = "0.3.1" http = "0.2.11" http-body = "0.4.5" hyper = "0.14.27" -ipnet = "2.9.0" -ndarray = { version = "0.15.6", optional = true } once_cell = "1.18.0" opentelemetry = { version = "0.19", features = ["rt-tokio"] } opentelemetry-otlp = { version = "0.12.0", features = ["metrics"] } @@ -76,7 +74,6 @@ surrealdb = { version = "1", path = "lib", features = [ "protocol-ws", "rustls", ] } -surrealml-core = { version = "0.0.7", optional = true } tempfile = "3.8.1" thiserror = "1.0.50" tokio = { version = "1.34.0", features = ["macros", "signal"] } diff --git a/Makefile.ci.toml b/Makefile.ci.toml index 4d1dfacd..809fea21 100644 --- a/Makefile.ci.toml +++ b/Makefile.ci.toml @@ -10,11 +10,13 @@ args = ["check", "--locked", "--workspace"] [tasks.ci-check-wasm] category = "CI - CHECK" command = "cargo" +env = { RUSTFLAGS = "--cfg surrealdb_unstable" } args = ["check", "--locked", "--package", "surrealdb", "--features", "protocol-ws,protocol-http,kv-mem,kv-indxdb,http,jwks", "--target", "wasm32-unknown-unknown"] [tasks.ci-clippy] category = "CI - CHECK" command = "cargo" +env = { RUSTFLAGS = "--cfg surrealdb_unstable" } args = ["clippy", "--all-targets", "--features", "storage-mem,storage-rocksdb,storage-speedb,storage-tikv,storage-fdb,scripting,http,jwks", "--tests", "--benches", "--examples","--bins", "--", "-D", "warnings"] # @@ -24,30 +26,31 @@ args = ["clippy", "--all-targets", "--features", "storage-mem,storage-rocksdb,st [tasks.ci-cli-integration] category = "CI - INTEGRATION TESTS" command = "cargo" -env = { RUST_LOG={ value = "cli_integration=debug", condition = { env_not_set = ["RUST_LOG"] } } } +env = { RUSTFLAGS = "--cfg surrealdb_unstable", RUST_LOG={ value = "cli_integration=debug", condition = { env_not_set = ["RUST_LOG"] } } } args = ["test", "--locked", "--no-default-features", "--features", "storage-mem,http,scripting,jwks", "--workspace", "--test", "cli_integration", "--", "cli_integration", "--nocapture"] [tasks.ci-http-integration] category = "CI - INTEGRATION TESTS" command = "cargo" -env = { RUST_LOG={ value = "http_integration=debug", condition = { env_not_set = ["RUST_LOG"] } } } +env = { RUSTFLAGS = "--cfg surrealdb_unstable", RUST_LOG={ value = "http_integration=debug", condition = { env_not_set = ["RUST_LOG"] } } } args = ["test", "--locked", "--no-default-features", "--features", "storage-mem,http-compression,jwks", "--workspace", "--test", "http_integration", "--", "http_integration", "--nocapture"] [tasks.ci-ws-integration] category = "WS - INTEGRATION TESTS" command = "cargo" -env = { RUST_LOG={ value = "ws_integration=debug", condition = { env_not_set = ["RUST_LOG"] } } } -args = ["test", "--locked", "--no-default-features", "--features", "storage-mem", "--workspace", "--test", "ws_integration", "--", "ws_integration", "--nocapture"] +env = { RUSTFLAGS = "--cfg surrealdb_unstable", RUST_LOG={ value = "ws_integration=debug", condition = { env_not_set = ["RUST_LOG"] } } } +args = ["test", "--locked", "--no-default-features", "--features", "storage-mem,sql2", "--workspace", "--test", "ws_integration", "--", "ws_integration", "--nocapture"] [tasks.ci-ml-integration] category = "ML - INTEGRATION TESTS" command = "cargo" -env = { RUST_LOG={ value = "cli_integration::common=debug", condition = { env_not_set = ["RUST_LOG"] } } } -args = ["test", "--locked", "--features", "storage-mem,ml", "--workspace", "--test", "ml_integration", "--", "ml_integration", "--nocapture"] +env = { RUSTFLAGS = "--cfg surrealdb_unstable", RUST_LOG={ value = "cli_integration::common=debug", condition = { env_not_set = ["RUST_LOG"] } } } +args = ["test", "--locked", "--features", "storage-mem,ml,sql2", "--workspace", "--test", "ml_integration", "--", "ml_integration", "--nocapture"] [tasks.ci-workspace-coverage] category = "CI - INTEGRATION TESTS" command = "cargo" +env = { RUSTFLAGS = "--cfg surrealdb_unstable" } args = [ "llvm-cov", "--html", "--locked", "--no-default-features", "--features", "storage-mem,scripting,http,jwks", "--workspace", "--", "--skip", "api_integration", @@ -60,8 +63,9 @@ args = [ [tasks.test-experimental-parser] category = "CI - INTEGRATION TESTS" command = "cargo" +env = { RUSTFLAGS = "--cfg surrealdb_unstable", RUSTDOCFLAGS="--cfg surrealdb_unstable" } args = [ - "test", "--locked", "--no-default-features", "--features", "storage-mem,scripting,http,experimental-parser", "--workspace", "--", + "test", "--locked", "--no-default-features", "--features", "storage-mem,scripting,http,parser2", "--workspace", "--", "--skip", "api_integration", "--skip", "cli_integration", "--skip", "http_integration", @@ -71,7 +75,8 @@ args = [ [tasks.test-workspace-coverage-complete] category = "CI - INTEGRATION TESTS" command = "cargo" -args = ["llvm-cov", "--html", "--locked", "--no-default-features", "--features", "protocol-ws,protocol-http,kv-mem,kv-rocksdb", "--workspace"] +env = { RUSTFLAGS = "--cfg surrealdb_unstable" } +args = ["llvm-cov", "--html", "--locked", "--no-default-features", "--features", "sql2,protocol-ws,protocol-http,kv-mem,kv-rocksdb", "--workspace"] [tasks.ci-workspace-coverage-complete] env = { _START_SURREALDB_PATH = "memory" } @@ -85,16 +90,18 @@ run_task = { name = ["start-surrealdb", "test-workspace-coverage-complete", "sto [tasks.test-kvs] private = true command = "cargo" +env = { RUSTFLAGS = "--cfg surrealdb_unstable" } args = ["test", "--locked", "--package", "surrealdb", "--no-default-features", "--features", "${_TEST_FEATURES}", "--lib", "kvs"] [tasks.test-api-integration] private = true command = "cargo" +env = { RUSTFLAGS = "--cfg surrealdb_unstable" } args = ["test", "--locked", "--package", "surrealdb", "--no-default-features", "--features", "${_TEST_FEATURES}", "--test", "api", "api_integration::${_TEST_API_ENGINE}"] [tasks.ci-api-integration] -env = { _START_SURREALDB_PATH = "memory" } +env = { _START_SURREALDB_PATH = "memory", RUSTFLAGS = "--cfg surrealdb_unstable" } private = true run_task = { name = ["start-surrealdb", "test-api-integration", "stop-surrealdb"], fork = true } @@ -109,7 +116,7 @@ run_task = "ci-api-integration" [tasks.ci-api-integration-ws] category = "CI - INTEGRATION TESTS" -env = { _TEST_API_ENGINE = "ws", _TEST_FEATURES = "protocol-ws" } +env = { _TEST_API_ENGINE = "ws", _TEST_FEATURES = "protocol-ws,sql2", RUSTFLAGS = "--cfg surrealdb_unstable" } run_task = "ci-api-integration" [tasks.ci-api-integration-any] @@ -232,7 +239,8 @@ script = "kill $(cat /tmp/tiup.pid) || true" [tasks.build-surrealdb] category = "CI - BUILD" command = "cargo" -args = ["build", "--locked", "--no-default-features", "--features", "storage-mem"] +env = { RUSTFLAGS = "--cfg surrealdb_unstable" } +args = ["build", "--locked", "--no-default-features", "--features", "storage-mem,sql2"] # # Benchmarks - Common @@ -240,6 +248,7 @@ args = ["build", "--locked", "--no-default-features", "--features", "storage-mem [tasks.ci-bench] category = "CI - BENCHMARK" command = "cargo" +env = { RUSTFLAGS = "--cfg surrealdb_unstable" } args = ["bench", "--quiet", "--package", "surrealdb", "--no-default-features", "--features", "kv-mem,scripting,http,jwks", "${@}"] # @@ -251,11 +260,13 @@ BENCH_NUM_OPS = { value = "1000", condition = { env_not_set = ["BENCH_NUM_OPS"] BENCH_DURATION = { value = "30", condition = { env_not_set = ["BENCH_DURATION"] } } BENCH_SAMPLE_SIZE = { value = "10", condition = { env_not_set = ["BENCH_SAMPLE_SIZE"] } } BENCH_FEATURES = { value = "protocol-ws,kv-mem,kv-rocksdb,kv-fdb-7_1", condition = { env_not_set = ["BENCH_FEATURES"] } } +RUSTFLAGS = "--cfg surrealdb_unstable" [tasks.bench-target] private = true category = "CI - BENCHMARK - SurrealDB Target" command = "cargo" +env = { RUSTFLAGS = "--cfg surrealdb_unstable" } args = ["bench", "--package", "surrealdb", "--bench", "sdb", "--no-default-features", "--features", "${BENCH_FEATURES}", "${@}"] [tasks.bench-lib-mem] diff --git a/Makefile.local.toml b/Makefile.local.toml index 2a256bd2..934417ac 100644 --- a/Makefile.local.toml +++ b/Makefile.local.toml @@ -17,19 +17,21 @@ dependencies = ["cargo-upgrade", "cargo-update"] [tasks.docs] category = "LOCAL USAGE" command = "cargo" -args = ["doc", "--open", "--no-deps", "--package", "surrealdb", "--features", "rustls,native-tls,protocol-ws,protocol-http,kv-mem,kv-indxdb,kv-speedb,kv-rocksdb,kv-tikv,http,scripting,jwks"] +env = { RUSTFLAGS = "--cfg surrealdb_unstable" } +args = ["doc", "--open", "--no-deps", "--package", "surrealdb", "--features", "rustls,native-tls,protocol-ws,protocol-http,kv-mem,kv-speedb,kv-rocksdb,kv-tikv,http,scripting,jwks"] # Test [tasks.test] category = "LOCAL USAGE" command = "cargo" -env = { RUST_MIN_STACK={ value = "4194304", condition = { env_not_set = ["RUST_MIN_STACK"] } } } +env = { RUSTFLAGS = "--cfg surrealdb_unstable", RUST_MIN_STACK={ value = "4194304", condition = { env_not_set = ["RUST_MIN_STACK"] } } } args = ["test", "--workspace", "--no-fail-fast"] # Check [tasks.cargo-check] category = "LOCAL USAGE" command = "cargo" +env = { RUSTFLAGS = "--cfg surrealdb_unstable" } args = ["check", "--workspace", "--features", "${DEV_FEATURES}"] [tasks.cargo-fmt] @@ -42,12 +44,13 @@ category = "LOCAL USAGE" script = """ set -e cd ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/ - cargo fmt --all --check -- ./lib/tests/**/*.rs ./lib/src/kvs/tests/*.rs + cargo fmt --all --check -- ./lib/tests/**/*.rs ./core/src/kvs/tests/*.rs """ [tasks.cargo-clippy] category = "LOCAL USAGE" command = "cargo" +env = { RUSTFLAGS = "--cfg surrealdb_unstable" } args = ["clippy", "--all-targets", "--all-features", "--", "-D", "warnings"] [tasks.check] @@ -68,24 +71,28 @@ args = ["clean"] [tasks.bench] category = "LOCAL USAGE" command = "cargo" +env = { RUSTFLAGS = "--cfg surrealdb_unstable" } args = ["bench", "--package", "surrealdb", "--no-default-features", "--features", "kv-mem,http,scripting,jwks", "--", "${@}"] # Run [tasks.run] category = "LOCAL USAGE" command = "cargo" +env = { RUSTFLAGS = "--cfg surrealdb_unstable" } args = ["run", "--no-default-features", "--features", "${DEV_FEATURES}", "--", "${@}"] # Serve [tasks.serve] category = "LOCAL USAGE" command = "cargo" +env = { RUSTFLAGS = "--cfg surrealdb_unstable" } args = ["run", "--no-default-features", "--features", "${DEV_FEATURES}", "--", "start", "--allow-all", "${@}"] # SQL [tasks.sql] category = "LOCAL USAGE" command = "cargo" +env = { RUSTFLAGS = "--cfg surrealdb_unstable" } args = ["run", "--no-default-features", "--features", "${DEV_FEATURES}", "--", "sql", "--conn", "ws://0.0.0.0:8000", "--multi", "--pretty", "${@}"] # Quick @@ -98,6 +105,7 @@ args = ["build", "${@}"] [tasks.build] category = "LOCAL USAGE" command = "cargo" +env = { RUSTFLAGS = "--cfg surrealdb_unstable" } args = ["build", "--release", "${@}"] # Default diff --git a/cackle.toml b/cackle.toml index e93fc730..8fcff8c4 100644 --- a/cackle.toml +++ b/cackle.toml @@ -29,6 +29,7 @@ include = [ "mio::net", "surreal::net", "surrealdb", + "surrealdb_core", "tokio::net", "tracing", "tracing_core", @@ -80,6 +81,12 @@ build.allow_build_instructions = [ ] allow_unsafe = true +[pkg.lru] +allow_unsafe = true +allow_apis = [ + "net", +] + [pkg.bzip2-sys] build.allow_apis = [ "fs", @@ -1151,11 +1158,16 @@ allow_apis = [ ] [pkg.surrealdb] -allow_unsafe = true allow_apis = [ "fs", ] +[pkg.surrealdb-core] +allow_unsafe = true +allow_apis = [ + "net", +] + [pkg.assert_fs] from.test.allow_apis = [ "fs", diff --git a/core/Cargo.toml b/core/Cargo.toml new file mode 100644 index 00000000..24528977 --- /dev/null +++ b/core/Cargo.toml @@ -0,0 +1,187 @@ +[package] +name = "surrealdb-core" +publish = true +edition = "2021" +version = "1.1.1" +rust-version = "1.70.0" +readme = "../lib/CARGO.md" +authors = ["Tobie Morgan Hitchcock "] +description = "A scalable, distributed, collaborative, document-graph database, for the realtime web" +repository = "https://github.com/surrealdb/surrealdb" +homepage = "https://github.com/surrealdb/surrealdb" +documentation = "https://docs.rs/surrealdb-core/" +keywords = [ + "database", + "embedded-database", + "key-value", + "key-value-store", + "kv-store", +] +categories = ["database-implementations", "data-structures", "embedded"] +license-file = "../LICENSE" +resolver = "2" + +[features] +# Public features +default = ["kv-mem"] +kv-mem = ["dep:echodb", "tokio/time"] +kv-indxdb = ["dep:indxdb"] +kv-speedb = ["dep:speedb", "tokio/time"] +kv-rocksdb = ["dep:rocksdb", "tokio/time"] +kv-tikv = ["dep:tikv"] +kv-fdb-5_1 = ["foundationdb/fdb-5_1", "kv-fdb"] +kv-fdb-5_2 = ["foundationdb/fdb-5_2", "kv-fdb"] +kv-fdb-6_0 = ["foundationdb/fdb-6_0", "kv-fdb"] +kv-fdb-6_1 = ["foundationdb/fdb-6_1", "kv-fdb"] +kv-fdb-6_2 = ["foundationdb/fdb-6_2", "kv-fdb"] +kv-fdb-6_3 = ["foundationdb/fdb-6_3", "kv-fdb"] +kv-fdb-7_0 = ["foundationdb/fdb-7_0", "kv-fdb"] +kv-fdb-7_1 = ["foundationdb/fdb-7_1", "kv-fdb"] +scripting = ["dep:js"] +http = ["dep:reqwest"] +ml = ["dep:surrealml-core", "dep:ndarray"] +jwks = ["dep:reqwest"] +arbitrary = [ + "dep:arbitrary", + "dep:regex-syntax", + "rust_decimal/rust-fuzz", + "geo-types/arbitrary", + "uuid/arbitrary", +] +experimental-parser = ["dep:phf", "dep:unicase"] +sql2 = [] +# Private features +kv-fdb = ["tokio/time"] + +[package.metadata.docs.rs] +rustdoc-args = ["--cfg", "docsrs"] +features = [ + "kv-mem", + "kv-indxdb", + "kv-rocksdb", + "http", + "scripting", + "jwks", +] +targets = [] + +[dependencies] +addr = { version = "0.15.6", default-features = false, features = ["std"] } +argon2 = "0.5.2" +ascii = { version = "0.3.2", package = "any_ascii" } +async-recursion = "1.0.5" +base64_lib = { version = "0.21.5", package = "base64" } +bcrypt = "0.15.0" +bincode = "1.3.3" +bytes = "1.5.0" +cedar-policy = "2.4.2" +channel = { version = "1.9.0", package = "async-channel" } +chrono = { version = "0.4.31", features = ["serde"] } +derive = { version = "0.12.0", package = "surrealdb-derive" } +deunicode = "1.4.1" +dmp = "0.2.0" +echodb = { version = "0.4.0", optional = true } +executor = { version = "1.8.0", package = "async-executor" } +foundationdb = { version = "0.8.0", default-features = false, features = [ + "embedded-fdb-include", +], optional = true } +fst = "0.4.7" +futures = "0.3.29" +fuzzy-matcher = "0.3.7" +geo = { version = "0.27.0", features = ["use-serde"] } +hex = { version = "0.4.3" } +indxdb = { version = "0.4.0", optional = true } +ipnet = "2.9.0" +js = { version = "0.4.2", package = "rquickjs", features = [ + "array-buffer", + "bindgen", + "classes", + "futures", + "loader", + "macro", + "parallel", + "properties", + "rust-alloc", +], optional = true } +jsonwebtoken = { version = "8.3.0-surreal.1", package = "surrealdb-jsonwebtoken" } +lexicmp = "0.1.0" +md-5 = "0.10.6" +nanoid = "0.4.0" +ndarray = { version = "0.15.6", optional = true } +nom = { version = "7.1.3", features = ["alloc"] } +num_cpus = "1.16.0" +object_store = { version = "0.8.0", optional = false } +once_cell = "1.18.0" +pbkdf2 = { version = "0.12.2", features = ["simple"] } +pin-project-lite = "0.2.13" +quick_cache = "0.4.0" +radix_trie = { version = "0.2.1", features = ["serde"] } +rand = "0.8.5" +regex = "1.10.2" +reqwest = { version = "0.11.22", default-features = false, features = [ + "json", + "stream", + "multipart", +], optional = true } +revision = "0.5.0" +roaring = { version = "0.10.2", features = ["serde"] } +rocksdb = { version = "0.21.0", features = ["lz4", "snappy"], optional = true } +rust_decimal = { version = "1.33.1", features = ["maths", "serde-str"] } +rust-stemmers = "1.2.0" +scrypt = "0.11.0" +semver = { version = "1.0.20", features = ["serde"] } +serde = { version = "1.0.193", features = ["derive"] } +serde_json = "1.0.108" +sha1 = "0.10.6" +sha2 = "0.10.8" +snap = "1.1.0" +speedb = { version = "0.0.4", features = ["lz4", "snappy"], optional = true } +storekey = "0.5.0" +surrealml-core = { version = "0.0.7", optional = true } +thiserror = "1.0.50" +tikv = { version = "0.2.0-surreal.2", default-features = false, package = "surrealdb-tikv-client", optional = true } +tracing = "0.1.40" +trice = "0.4.0" +ulid = { version = "1.1.0", features = ["serde"] } +url = "2.5.0" +phf = { version = "0.11.2", features = ["macros", "unicase"], optional = true } +unicase = { version = "2.7.0", optional = true } +arbitrary = { version = "1.3.2", features = ["derive"], optional = true } +regex-syntax = { version = "0.8.2", optional = true, features = ["arbitrary"] } +geo-types = { version = "0.7.12", features = ["arbitrary"] } + +[dev-dependencies] +criterion = { version = "0.5.1", features = ["async_tokio"] } +env_logger = "0.10.1" +pprof = { version = "0.13.0", features = ["flamegraph", "criterion"] } +serial_test = "2.0.0" +temp-dir = "0.1.11" +test-log = { version = "0.2.13", features = ["trace"] } +time = { version = "0.3.30", features = ["serde"] } +tokio = { version = "1.34.0", features = ["macros", "sync", "rt-multi-thread"] } +tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } +wiremock = "0.5.22" + +[target.'cfg(target_arch = "wasm32")'.dependencies] +pharos = "0.5.3" +ring = { version = "0.17.7", features = ["wasm32_unknown_unknown_js"] } # Make ring-based dependencies work on Wasm +tokio = { version = "1.34.0", default-features = false, features = ["rt", "sync"] } +uuid = { version = "1.6.1", features = ["serde", "js", "v4", "v7"] } +wasm-bindgen-futures = "0.4.39" +wasmtimer = { version = "0.2.0", default-features = false, features = [ + "tokio", +] } +ws_stream_wasm = "0.7.4" + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +tokio = { version = "1.34.0", default-features = false, features = [ + "macros", + "io-util", + "io-std", + "fs", + "rt-multi-thread", + "time", + "sync", +] } +tokio-tungstenite = { version = "0.20.1", optional = true } +uuid = { version = "1.6.1", features = ["serde", "v4", "v7"] } diff --git a/lib/src/cf/gc.rs b/core/src/cf/gc.rs similarity index 100% rename from lib/src/cf/gc.rs rename to core/src/cf/gc.rs diff --git a/lib/src/cf/mod.rs b/core/src/cf/mod.rs similarity index 100% rename from lib/src/cf/mod.rs rename to core/src/cf/mod.rs diff --git a/lib/src/cf/mutations.rs b/core/src/cf/mutations.rs similarity index 100% rename from lib/src/cf/mutations.rs rename to core/src/cf/mutations.rs diff --git a/lib/src/cf/reader.rs b/core/src/cf/reader.rs similarity index 100% rename from lib/src/cf/reader.rs rename to core/src/cf/reader.rs diff --git a/lib/src/cf/writer.rs b/core/src/cf/writer.rs similarity index 100% rename from lib/src/cf/writer.rs rename to core/src/cf/writer.rs diff --git a/lib/src/cnf/mod.rs b/core/src/cnf/mod.rs similarity index 97% rename from lib/src/cnf/mod.rs rename to core/src/cnf/mod.rs index 8f6f619e..0ad27711 100644 --- a/lib/src/cnf/mod.rs +++ b/core/src/cnf/mod.rs @@ -6,7 +6,7 @@ use once_cell::sync::Lazy; pub const MAX_CONCURRENT_TASKS: usize = 64; /// Specifies how deep various forms of computation will go before the query fails -/// with [`crate::error::Db::ComputationDepthExceeded`]. +/// with [`crate::err::Error::ComputationDepthExceeded`]. /// /// For reference, use ~15 per MiB of stack in release mode. /// diff --git a/lib/src/ctx/cancellation.rs b/core/src/ctx/cancellation.rs similarity index 100% rename from lib/src/ctx/cancellation.rs rename to core/src/ctx/cancellation.rs diff --git a/lib/src/ctx/canceller.rs b/core/src/ctx/canceller.rs similarity index 100% rename from lib/src/ctx/canceller.rs rename to core/src/ctx/canceller.rs diff --git a/lib/src/ctx/context.rs b/core/src/ctx/context.rs similarity index 100% rename from lib/src/ctx/context.rs rename to core/src/ctx/context.rs diff --git a/lib/src/ctx/mod.rs b/core/src/ctx/mod.rs similarity index 100% rename from lib/src/ctx/mod.rs rename to core/src/ctx/mod.rs diff --git a/lib/src/ctx/reason.rs b/core/src/ctx/reason.rs similarity index 100% rename from lib/src/ctx/reason.rs rename to core/src/ctx/reason.rs diff --git a/lib/src/dbs/capabilities.rs b/core/src/dbs/capabilities.rs similarity index 86% rename from lib/src/dbs/capabilities.rs rename to core/src/dbs/capabilities.rs index 3347e985..e3288cb7 100644 --- a/lib/src/dbs/capabilities.rs +++ b/core/src/dbs/capabilities.rs @@ -153,67 +153,6 @@ impl std::fmt::Display fo } } -/// Capabilities are used to limit what a user can do to the system. -/// -/// Capabilities are split into 4 categories: -/// - Scripting: Whether or not the user can execute scripts -/// - Guest access: Whether or not a non-authenticated user can execute queries on the system when authentication is enabled. -/// - Functions: Whether or not the user can execute certain functions -/// - Network: Whether or not the user can access certain network addresses -/// -/// Capabilities are configured globally. By default, capabilities are configured as: -/// - Scripting: false -/// - Guest access: false -/// - Functions: All functions are allowed -/// - Network: No network address is allowed nor denied, hence all network addresses are denied unless explicitly allowed -/// -/// The capabilities are defined using allow/deny lists for fine-grained control. -/// -/// Examples: -/// - Allow all functions: `--allow-funcs` -/// - Allow all functions except `http.*`: `--allow-funcs --deny-funcs 'http.*'` -/// - Allow all network addresses except AWS metadata endpoint: `--allow-net --deny-net='169.254.169.254'` -/// -/// # Examples -/// -/// Create a new instance, and allow all capabilities -#[cfg_attr(feature = "kv-rocksdb", doc = "```no_run")] -#[cfg_attr(not(feature = "kv-rocksdb"), doc = "```ignore")] -/// # use surrealdb::opt::capabilities::Capabilities; -/// # use surrealdb::opt::Config; -/// # use surrealdb::Surreal; -/// # use surrealdb::engine::local::File; -/// # #[tokio::main] -/// # async fn main() -> surrealdb::Result<()> { -/// let capabilities = Capabilities::all(); -/// let config = Config::default().capabilities(capabilities); -/// let db = Surreal::new::(("temp.db", config)).await?; -/// # Ok(()) -/// # } -/// ``` -/// -/// Create a new instance, and allow certain functions -#[cfg_attr(feature = "kv-rocksdb", doc = "```no_run")] -#[cfg_attr(not(feature = "kv-rocksdb"), doc = "```ignore")] -/// # use std::str::FromStr; -/// # use surrealdb::engine::local::File; -/// # use surrealdb::opt::capabilities::Capabilities; -/// # use surrealdb::opt::capabilities::FuncTarget; -/// # use surrealdb::opt::capabilities::Targets; -/// # use surrealdb::opt::Config; -/// # use surrealdb::Surreal; -/// # #[tokio::main] -/// # async fn main() -> surrealdb::Result<()> { -/// let capabilities = Capabilities::default() -/// .with_functions(Targets::::All) -/// .without_functions(Targets::::Some( -/// [FuncTarget::from_str("http::*").unwrap()].into(), -/// )); -/// let config = Config::default().capabilities(capabilities); -/// let db = Surreal::new::(("temp.db", config)).await?; -/// # Ok(()) -/// # } -/// ``` #[derive(Debug, Clone)] pub struct Capabilities { scripting: bool, diff --git a/lib/src/dbs/distinct.rs b/core/src/dbs/distinct.rs similarity index 100% rename from lib/src/dbs/distinct.rs rename to core/src/dbs/distinct.rs diff --git a/lib/src/dbs/executor.rs b/core/src/dbs/executor.rs similarity index 100% rename from lib/src/dbs/executor.rs rename to core/src/dbs/executor.rs diff --git a/lib/src/dbs/explanation.rs b/core/src/dbs/explanation.rs similarity index 100% rename from lib/src/dbs/explanation.rs rename to core/src/dbs/explanation.rs diff --git a/lib/src/dbs/iterator.rs b/core/src/dbs/iterator.rs similarity index 100% rename from lib/src/dbs/iterator.rs rename to core/src/dbs/iterator.rs diff --git a/lib/src/dbs/mod.rs b/core/src/dbs/mod.rs similarity index 100% rename from lib/src/dbs/mod.rs rename to core/src/dbs/mod.rs diff --git a/lib/src/dbs/node.rs b/core/src/dbs/node.rs similarity index 100% rename from lib/src/dbs/node.rs rename to core/src/dbs/node.rs diff --git a/lib/src/dbs/notification.rs b/core/src/dbs/notification.rs similarity index 100% rename from lib/src/dbs/notification.rs rename to core/src/dbs/notification.rs diff --git a/lib/src/dbs/options.rs b/core/src/dbs/options.rs similarity index 100% rename from lib/src/dbs/options.rs rename to core/src/dbs/options.rs diff --git a/lib/src/dbs/processor.rs b/core/src/dbs/processor.rs similarity index 100% rename from lib/src/dbs/processor.rs rename to core/src/dbs/processor.rs diff --git a/lib/src/dbs/response.rs b/core/src/dbs/response.rs similarity index 97% rename from lib/src/dbs/response.rs rename to core/src/dbs/response.rs index 57587364..23bf0b39 100644 --- a/lib/src/dbs/response.rs +++ b/core/src/dbs/response.rs @@ -40,7 +40,8 @@ impl Response { #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "UPPERCASE")] -pub(crate) enum Status { +#[doc(hidden)] +pub enum Status { Ok, Err, } diff --git a/lib/src/dbs/session.rs b/core/src/dbs/session.rs similarity index 100% rename from lib/src/dbs/session.rs rename to core/src/dbs/session.rs diff --git a/lib/src/dbs/statement.rs b/core/src/dbs/statement.rs similarity index 100% rename from lib/src/dbs/statement.rs rename to core/src/dbs/statement.rs diff --git a/lib/src/dbs/test.rs b/core/src/dbs/test.rs similarity index 100% rename from lib/src/dbs/test.rs rename to core/src/dbs/test.rs diff --git a/lib/src/dbs/transaction.rs b/core/src/dbs/transaction.rs similarity index 100% rename from lib/src/dbs/transaction.rs rename to core/src/dbs/transaction.rs diff --git a/lib/src/dbs/variables.rs b/core/src/dbs/variables.rs similarity index 100% rename from lib/src/dbs/variables.rs rename to core/src/dbs/variables.rs diff --git a/lib/src/doc/allow.rs b/core/src/doc/allow.rs similarity index 100% rename from lib/src/doc/allow.rs rename to core/src/doc/allow.rs diff --git a/lib/src/doc/alter.rs b/core/src/doc/alter.rs similarity index 100% rename from lib/src/doc/alter.rs rename to core/src/doc/alter.rs diff --git a/lib/src/doc/changefeeds.rs b/core/src/doc/changefeeds.rs similarity index 100% rename from lib/src/doc/changefeeds.rs rename to core/src/doc/changefeeds.rs diff --git a/lib/src/doc/check.rs b/core/src/doc/check.rs similarity index 100% rename from lib/src/doc/check.rs rename to core/src/doc/check.rs diff --git a/lib/src/doc/clean.rs b/core/src/doc/clean.rs similarity index 100% rename from lib/src/doc/clean.rs rename to core/src/doc/clean.rs diff --git a/lib/src/doc/compute.rs b/core/src/doc/compute.rs similarity index 100% rename from lib/src/doc/compute.rs rename to core/src/doc/compute.rs diff --git a/lib/src/doc/create.rs b/core/src/doc/create.rs similarity index 100% rename from lib/src/doc/create.rs rename to core/src/doc/create.rs diff --git a/lib/src/doc/delete.rs b/core/src/doc/delete.rs similarity index 100% rename from lib/src/doc/delete.rs rename to core/src/doc/delete.rs diff --git a/lib/src/doc/document.rs b/core/src/doc/document.rs similarity index 100% rename from lib/src/doc/document.rs rename to core/src/doc/document.rs diff --git a/lib/src/doc/edges.rs b/core/src/doc/edges.rs similarity index 100% rename from lib/src/doc/edges.rs rename to core/src/doc/edges.rs diff --git a/lib/src/doc/empty.rs b/core/src/doc/empty.rs similarity index 100% rename from lib/src/doc/empty.rs rename to core/src/doc/empty.rs diff --git a/lib/src/doc/erase.rs b/core/src/doc/erase.rs similarity index 100% rename from lib/src/doc/erase.rs rename to core/src/doc/erase.rs diff --git a/lib/src/doc/event.rs b/core/src/doc/event.rs similarity index 100% rename from lib/src/doc/event.rs rename to core/src/doc/event.rs diff --git a/lib/src/doc/field.rs b/core/src/doc/field.rs similarity index 100% rename from lib/src/doc/field.rs rename to core/src/doc/field.rs diff --git a/lib/src/doc/index.rs b/core/src/doc/index.rs similarity index 100% rename from lib/src/doc/index.rs rename to core/src/doc/index.rs diff --git a/lib/src/doc/insert.rs b/core/src/doc/insert.rs similarity index 100% rename from lib/src/doc/insert.rs rename to core/src/doc/insert.rs diff --git a/lib/src/doc/lives.rs b/core/src/doc/lives.rs similarity index 100% rename from lib/src/doc/lives.rs rename to core/src/doc/lives.rs diff --git a/lib/src/doc/merge.rs b/core/src/doc/merge.rs similarity index 100% rename from lib/src/doc/merge.rs rename to core/src/doc/merge.rs diff --git a/lib/src/doc/mod.rs b/core/src/doc/mod.rs similarity index 100% rename from lib/src/doc/mod.rs rename to core/src/doc/mod.rs diff --git a/lib/src/doc/pluck.rs b/core/src/doc/pluck.rs similarity index 100% rename from lib/src/doc/pluck.rs rename to core/src/doc/pluck.rs diff --git a/lib/src/doc/process.rs b/core/src/doc/process.rs similarity index 100% rename from lib/src/doc/process.rs rename to core/src/doc/process.rs diff --git a/lib/src/doc/purge.rs b/core/src/doc/purge.rs similarity index 100% rename from lib/src/doc/purge.rs rename to core/src/doc/purge.rs diff --git a/lib/src/doc/relate.rs b/core/src/doc/relate.rs similarity index 100% rename from lib/src/doc/relate.rs rename to core/src/doc/relate.rs diff --git a/lib/src/doc/reset.rs b/core/src/doc/reset.rs similarity index 100% rename from lib/src/doc/reset.rs rename to core/src/doc/reset.rs diff --git a/lib/src/doc/select.rs b/core/src/doc/select.rs similarity index 100% rename from lib/src/doc/select.rs rename to core/src/doc/select.rs diff --git a/lib/src/doc/store.rs b/core/src/doc/store.rs similarity index 100% rename from lib/src/doc/store.rs rename to core/src/doc/store.rs diff --git a/lib/src/doc/table.rs b/core/src/doc/table.rs similarity index 100% rename from lib/src/doc/table.rs rename to core/src/doc/table.rs diff --git a/lib/src/doc/update.rs b/core/src/doc/update.rs similarity index 100% rename from lib/src/doc/update.rs rename to core/src/doc/update.rs diff --git a/lib/src/env/mod.rs b/core/src/env/mod.rs similarity index 100% rename from lib/src/env/mod.rs rename to core/src/env/mod.rs diff --git a/lib/src/err/mod.rs b/core/src/err/mod.rs similarity index 100% rename from lib/src/err/mod.rs rename to core/src/err/mod.rs diff --git a/lib/src/exe/mod.rs b/core/src/exe/mod.rs similarity index 100% rename from lib/src/exe/mod.rs rename to core/src/exe/mod.rs diff --git a/lib/src/exe/spawn.rs b/core/src/exe/spawn.rs similarity index 100% rename from lib/src/exe/spawn.rs rename to core/src/exe/spawn.rs diff --git a/lib/src/exe/try_join_all_buffered.rs b/core/src/exe/try_join_all_buffered.rs similarity index 100% rename from lib/src/exe/try_join_all_buffered.rs rename to core/src/exe/try_join_all_buffered.rs diff --git a/lib/src/fflags.rs b/core/src/fflags.rs similarity index 100% rename from lib/src/fflags.rs rename to core/src/fflags.rs diff --git a/lib/src/fnc/args.rs b/core/src/fnc/args.rs similarity index 100% rename from lib/src/fnc/args.rs rename to core/src/fnc/args.rs diff --git a/lib/src/fnc/array.rs b/core/src/fnc/array.rs similarity index 100% rename from lib/src/fnc/array.rs rename to core/src/fnc/array.rs diff --git a/lib/src/fnc/bytes.rs b/core/src/fnc/bytes.rs similarity index 100% rename from lib/src/fnc/bytes.rs rename to core/src/fnc/bytes.rs diff --git a/lib/src/fnc/count.rs b/core/src/fnc/count.rs similarity index 100% rename from lib/src/fnc/count.rs rename to core/src/fnc/count.rs diff --git a/lib/src/fnc/crypto.rs b/core/src/fnc/crypto.rs similarity index 100% rename from lib/src/fnc/crypto.rs rename to core/src/fnc/crypto.rs diff --git a/lib/src/fnc/duration.rs b/core/src/fnc/duration.rs similarity index 100% rename from lib/src/fnc/duration.rs rename to core/src/fnc/duration.rs diff --git a/lib/src/fnc/encoding.rs b/core/src/fnc/encoding.rs similarity index 100% rename from lib/src/fnc/encoding.rs rename to core/src/fnc/encoding.rs diff --git a/lib/src/fnc/geo.rs b/core/src/fnc/geo.rs similarity index 100% rename from lib/src/fnc/geo.rs rename to core/src/fnc/geo.rs diff --git a/lib/src/fnc/http.rs b/core/src/fnc/http.rs similarity index 100% rename from lib/src/fnc/http.rs rename to core/src/fnc/http.rs diff --git a/lib/src/fnc/math.rs b/core/src/fnc/math.rs similarity index 100% rename from lib/src/fnc/math.rs rename to core/src/fnc/math.rs diff --git a/lib/src/fnc/meta.rs b/core/src/fnc/meta.rs similarity index 100% rename from lib/src/fnc/meta.rs rename to core/src/fnc/meta.rs diff --git a/lib/src/fnc/mod.rs b/core/src/fnc/mod.rs similarity index 100% rename from lib/src/fnc/mod.rs rename to core/src/fnc/mod.rs diff --git a/lib/src/fnc/not.rs b/core/src/fnc/not.rs similarity index 100% rename from lib/src/fnc/not.rs rename to core/src/fnc/not.rs diff --git a/lib/src/fnc/object.rs b/core/src/fnc/object.rs similarity index 100% rename from lib/src/fnc/object.rs rename to core/src/fnc/object.rs diff --git a/lib/src/fnc/operate.rs b/core/src/fnc/operate.rs similarity index 100% rename from lib/src/fnc/operate.rs rename to core/src/fnc/operate.rs diff --git a/lib/src/fnc/parse.rs b/core/src/fnc/parse.rs similarity index 100% rename from lib/src/fnc/parse.rs rename to core/src/fnc/parse.rs diff --git a/lib/src/fnc/rand.rs b/core/src/fnc/rand.rs similarity index 100% rename from lib/src/fnc/rand.rs rename to core/src/fnc/rand.rs diff --git a/lib/src/fnc/script/classes/duration.rs b/core/src/fnc/script/classes/duration.rs similarity index 100% rename from lib/src/fnc/script/classes/duration.rs rename to core/src/fnc/script/classes/duration.rs diff --git a/lib/src/fnc/script/classes/mod.rs b/core/src/fnc/script/classes/mod.rs similarity index 100% rename from lib/src/fnc/script/classes/mod.rs rename to core/src/fnc/script/classes/mod.rs diff --git a/lib/src/fnc/script/classes/record.rs b/core/src/fnc/script/classes/record.rs similarity index 100% rename from lib/src/fnc/script/classes/record.rs rename to core/src/fnc/script/classes/record.rs diff --git a/lib/src/fnc/script/classes/uuid.rs b/core/src/fnc/script/classes/uuid.rs similarity index 100% rename from lib/src/fnc/script/classes/uuid.rs rename to core/src/fnc/script/classes/uuid.rs diff --git a/lib/src/fnc/script/error.rs b/core/src/fnc/script/error.rs similarity index 100% rename from lib/src/fnc/script/error.rs rename to core/src/fnc/script/error.rs diff --git a/lib/src/fnc/script/fetch/body.rs b/core/src/fnc/script/fetch/body.rs similarity index 100% rename from lib/src/fnc/script/fetch/body.rs rename to core/src/fnc/script/fetch/body.rs diff --git a/lib/src/fnc/script/fetch/classes/blob.rs b/core/src/fnc/script/fetch/classes/blob.rs similarity index 100% rename from lib/src/fnc/script/fetch/classes/blob.rs rename to core/src/fnc/script/fetch/classes/blob.rs diff --git a/lib/src/fnc/script/fetch/classes/form_data.rs b/core/src/fnc/script/fetch/classes/form_data.rs similarity index 100% rename from lib/src/fnc/script/fetch/classes/form_data.rs rename to core/src/fnc/script/fetch/classes/form_data.rs diff --git a/lib/src/fnc/script/fetch/classes/headers.rs b/core/src/fnc/script/fetch/classes/headers.rs similarity index 100% rename from lib/src/fnc/script/fetch/classes/headers.rs rename to core/src/fnc/script/fetch/classes/headers.rs diff --git a/lib/src/fnc/script/fetch/classes/mod.rs b/core/src/fnc/script/fetch/classes/mod.rs similarity index 100% rename from lib/src/fnc/script/fetch/classes/mod.rs rename to core/src/fnc/script/fetch/classes/mod.rs diff --git a/lib/src/fnc/script/fetch/classes/request.rs b/core/src/fnc/script/fetch/classes/request.rs similarity index 100% rename from lib/src/fnc/script/fetch/classes/request.rs rename to core/src/fnc/script/fetch/classes/request.rs diff --git a/lib/src/fnc/script/fetch/classes/response/init.rs b/core/src/fnc/script/fetch/classes/response/init.rs similarity index 100% rename from lib/src/fnc/script/fetch/classes/response/init.rs rename to core/src/fnc/script/fetch/classes/response/init.rs diff --git a/lib/src/fnc/script/fetch/classes/response/mod.rs b/core/src/fnc/script/fetch/classes/response/mod.rs similarity index 100% rename from lib/src/fnc/script/fetch/classes/response/mod.rs rename to core/src/fnc/script/fetch/classes/response/mod.rs diff --git a/lib/src/fnc/script/fetch/func.rs b/core/src/fnc/script/fetch/func.rs similarity index 100% rename from lib/src/fnc/script/fetch/func.rs rename to core/src/fnc/script/fetch/func.rs diff --git a/lib/src/fnc/script/fetch/mod.rs b/core/src/fnc/script/fetch/mod.rs similarity index 100% rename from lib/src/fnc/script/fetch/mod.rs rename to core/src/fnc/script/fetch/mod.rs diff --git a/lib/src/fnc/script/fetch/stream.rs b/core/src/fnc/script/fetch/stream.rs similarity index 100% rename from lib/src/fnc/script/fetch/stream.rs rename to core/src/fnc/script/fetch/stream.rs diff --git a/lib/src/fnc/script/fetch/util.rs b/core/src/fnc/script/fetch/util.rs similarity index 100% rename from lib/src/fnc/script/fetch/util.rs rename to core/src/fnc/script/fetch/util.rs diff --git a/lib/src/fnc/script/fetch_stub/mod.rs b/core/src/fnc/script/fetch_stub/mod.rs similarity index 100% rename from lib/src/fnc/script/fetch_stub/mod.rs rename to core/src/fnc/script/fetch_stub/mod.rs diff --git a/lib/src/fnc/script/fetch_stub/test.rs b/core/src/fnc/script/fetch_stub/test.rs similarity index 100% rename from lib/src/fnc/script/fetch_stub/test.rs rename to core/src/fnc/script/fetch_stub/test.rs diff --git a/lib/src/fnc/script/from.rs b/core/src/fnc/script/from.rs similarity index 100% rename from lib/src/fnc/script/from.rs rename to core/src/fnc/script/from.rs diff --git a/lib/src/fnc/script/globals/console.rs b/core/src/fnc/script/globals/console.rs similarity index 100% rename from lib/src/fnc/script/globals/console.rs rename to core/src/fnc/script/globals/console.rs diff --git a/lib/src/fnc/script/globals/mod.rs b/core/src/fnc/script/globals/mod.rs similarity index 100% rename from lib/src/fnc/script/globals/mod.rs rename to core/src/fnc/script/globals/mod.rs diff --git a/lib/src/fnc/script/into.rs b/core/src/fnc/script/into.rs similarity index 100% rename from lib/src/fnc/script/into.rs rename to core/src/fnc/script/into.rs diff --git a/lib/src/fnc/script/main.rs b/core/src/fnc/script/main.rs similarity index 100% rename from lib/src/fnc/script/main.rs rename to core/src/fnc/script/main.rs diff --git a/lib/src/fnc/script/mod.rs b/core/src/fnc/script/mod.rs similarity index 100% rename from lib/src/fnc/script/mod.rs rename to core/src/fnc/script/mod.rs diff --git a/lib/src/fnc/script/modules/mod.rs b/core/src/fnc/script/modules/mod.rs similarity index 100% rename from lib/src/fnc/script/modules/mod.rs rename to core/src/fnc/script/modules/mod.rs diff --git a/lib/src/fnc/script/modules/os.rs b/core/src/fnc/script/modules/os.rs similarity index 100% rename from lib/src/fnc/script/modules/os.rs rename to core/src/fnc/script/modules/os.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/array.rs b/core/src/fnc/script/modules/surrealdb/functions/array.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/array.rs rename to core/src/fnc/script/modules/surrealdb/functions/array.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/array/sort.rs b/core/src/fnc/script/modules/surrealdb/functions/array/sort.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/array/sort.rs rename to core/src/fnc/script/modules/surrealdb/functions/array/sort.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/bytes.rs b/core/src/fnc/script/modules/surrealdb/functions/bytes.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/bytes.rs rename to core/src/fnc/script/modules/surrealdb/functions/bytes.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/crypto.rs b/core/src/fnc/script/modules/surrealdb/functions/crypto.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/crypto.rs rename to core/src/fnc/script/modules/surrealdb/functions/crypto.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/crypto/argon2.rs b/core/src/fnc/script/modules/surrealdb/functions/crypto/argon2.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/crypto/argon2.rs rename to core/src/fnc/script/modules/surrealdb/functions/crypto/argon2.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/crypto/bcrypt.rs b/core/src/fnc/script/modules/surrealdb/functions/crypto/bcrypt.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/crypto/bcrypt.rs rename to core/src/fnc/script/modules/surrealdb/functions/crypto/bcrypt.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/crypto/pbkdf2.rs b/core/src/fnc/script/modules/surrealdb/functions/crypto/pbkdf2.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/crypto/pbkdf2.rs rename to core/src/fnc/script/modules/surrealdb/functions/crypto/pbkdf2.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/crypto/scrypt.rs b/core/src/fnc/script/modules/surrealdb/functions/crypto/scrypt.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/crypto/scrypt.rs rename to core/src/fnc/script/modules/surrealdb/functions/crypto/scrypt.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/duration.rs b/core/src/fnc/script/modules/surrealdb/functions/duration.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/duration.rs rename to core/src/fnc/script/modules/surrealdb/functions/duration.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/duration/from.rs b/core/src/fnc/script/modules/surrealdb/functions/duration/from.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/duration/from.rs rename to core/src/fnc/script/modules/surrealdb/functions/duration/from.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/encoding.rs b/core/src/fnc/script/modules/surrealdb/functions/encoding.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/encoding.rs rename to core/src/fnc/script/modules/surrealdb/functions/encoding.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/encoding/base64.rs b/core/src/fnc/script/modules/surrealdb/functions/encoding/base64.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/encoding/base64.rs rename to core/src/fnc/script/modules/surrealdb/functions/encoding/base64.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/geo.rs b/core/src/fnc/script/modules/surrealdb/functions/geo.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/geo.rs rename to core/src/fnc/script/modules/surrealdb/functions/geo.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/geo/hash.rs b/core/src/fnc/script/modules/surrealdb/functions/geo/hash.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/geo/hash.rs rename to core/src/fnc/script/modules/surrealdb/functions/geo/hash.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/http.rs b/core/src/fnc/script/modules/surrealdb/functions/http.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/http.rs rename to core/src/fnc/script/modules/surrealdb/functions/http.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/math.rs b/core/src/fnc/script/modules/surrealdb/functions/math.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/math.rs rename to core/src/fnc/script/modules/surrealdb/functions/math.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/meta.rs b/core/src/fnc/script/modules/surrealdb/functions/meta.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/meta.rs rename to core/src/fnc/script/modules/surrealdb/functions/meta.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/mod.rs b/core/src/fnc/script/modules/surrealdb/functions/mod.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/mod.rs rename to core/src/fnc/script/modules/surrealdb/functions/mod.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/object.rs b/core/src/fnc/script/modules/surrealdb/functions/object.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/object.rs rename to core/src/fnc/script/modules/surrealdb/functions/object.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/parse.rs b/core/src/fnc/script/modules/surrealdb/functions/parse.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/parse.rs rename to core/src/fnc/script/modules/surrealdb/functions/parse.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/parse/email.rs b/core/src/fnc/script/modules/surrealdb/functions/parse/email.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/parse/email.rs rename to core/src/fnc/script/modules/surrealdb/functions/parse/email.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/parse/url.rs b/core/src/fnc/script/modules/surrealdb/functions/parse/url.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/parse/url.rs rename to core/src/fnc/script/modules/surrealdb/functions/parse/url.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/rand.rs b/core/src/fnc/script/modules/surrealdb/functions/rand.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/rand.rs rename to core/src/fnc/script/modules/surrealdb/functions/rand.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/rand/uuid.rs b/core/src/fnc/script/modules/surrealdb/functions/rand/uuid.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/rand/uuid.rs rename to core/src/fnc/script/modules/surrealdb/functions/rand/uuid.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/search.rs b/core/src/fnc/script/modules/surrealdb/functions/search.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/search.rs rename to core/src/fnc/script/modules/surrealdb/functions/search.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/session.rs b/core/src/fnc/script/modules/surrealdb/functions/session.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/session.rs rename to core/src/fnc/script/modules/surrealdb/functions/session.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/string.rs b/core/src/fnc/script/modules/surrealdb/functions/string.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/string.rs rename to core/src/fnc/script/modules/surrealdb/functions/string.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/string/distance.rs b/core/src/fnc/script/modules/surrealdb/functions/string/distance.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/string/distance.rs rename to core/src/fnc/script/modules/surrealdb/functions/string/distance.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/string/is.rs b/core/src/fnc/script/modules/surrealdb/functions/string/is.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/string/is.rs rename to core/src/fnc/script/modules/surrealdb/functions/string/is.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/string/semver.rs b/core/src/fnc/script/modules/surrealdb/functions/string/semver.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/string/semver.rs rename to core/src/fnc/script/modules/surrealdb/functions/string/semver.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/string/semver/inc.rs b/core/src/fnc/script/modules/surrealdb/functions/string/semver/inc.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/string/semver/inc.rs rename to core/src/fnc/script/modules/surrealdb/functions/string/semver/inc.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/string/semver/set.rs b/core/src/fnc/script/modules/surrealdb/functions/string/semver/set.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/string/semver/set.rs rename to core/src/fnc/script/modules/surrealdb/functions/string/semver/set.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/string/similarity.rs b/core/src/fnc/script/modules/surrealdb/functions/string/similarity.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/string/similarity.rs rename to core/src/fnc/script/modules/surrealdb/functions/string/similarity.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/time.rs b/core/src/fnc/script/modules/surrealdb/functions/time.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/time.rs rename to core/src/fnc/script/modules/surrealdb/functions/time.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/time/from.rs b/core/src/fnc/script/modules/surrealdb/functions/time/from.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/time/from.rs rename to core/src/fnc/script/modules/surrealdb/functions/time/from.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/type.rs b/core/src/fnc/script/modules/surrealdb/functions/type.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/type.rs rename to core/src/fnc/script/modules/surrealdb/functions/type.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/type/is.rs b/core/src/fnc/script/modules/surrealdb/functions/type/is.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/type/is.rs rename to core/src/fnc/script/modules/surrealdb/functions/type/is.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/vector.rs b/core/src/fnc/script/modules/surrealdb/functions/vector.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/vector.rs rename to core/src/fnc/script/modules/surrealdb/functions/vector.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/vector/distance.rs b/core/src/fnc/script/modules/surrealdb/functions/vector/distance.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/vector/distance.rs rename to core/src/fnc/script/modules/surrealdb/functions/vector/distance.rs diff --git a/lib/src/fnc/script/modules/surrealdb/functions/vector/similarity.rs b/core/src/fnc/script/modules/surrealdb/functions/vector/similarity.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/functions/vector/similarity.rs rename to core/src/fnc/script/modules/surrealdb/functions/vector/similarity.rs diff --git a/lib/src/fnc/script/modules/surrealdb/mod.rs b/core/src/fnc/script/modules/surrealdb/mod.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/mod.rs rename to core/src/fnc/script/modules/surrealdb/mod.rs diff --git a/lib/src/fnc/script/modules/surrealdb/query/classes.rs b/core/src/fnc/script/modules/surrealdb/query/classes.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/query/classes.rs rename to core/src/fnc/script/modules/surrealdb/query/classes.rs diff --git a/lib/src/fnc/script/modules/surrealdb/query/mod.rs b/core/src/fnc/script/modules/surrealdb/query/mod.rs similarity index 100% rename from lib/src/fnc/script/modules/surrealdb/query/mod.rs rename to core/src/fnc/script/modules/surrealdb/query/mod.rs diff --git a/lib/src/fnc/script/tests/fetch.rs b/core/src/fnc/script/tests/fetch.rs similarity index 100% rename from lib/src/fnc/script/tests/fetch.rs rename to core/src/fnc/script/tests/fetch.rs diff --git a/lib/src/fnc/script/tests/mod.rs b/core/src/fnc/script/tests/mod.rs similarity index 100% rename from lib/src/fnc/script/tests/mod.rs rename to core/src/fnc/script/tests/mod.rs diff --git a/lib/src/fnc/search.rs b/core/src/fnc/search.rs similarity index 100% rename from lib/src/fnc/search.rs rename to core/src/fnc/search.rs diff --git a/lib/src/fnc/session.rs b/core/src/fnc/session.rs similarity index 100% rename from lib/src/fnc/session.rs rename to core/src/fnc/session.rs diff --git a/lib/src/fnc/sleep.rs b/core/src/fnc/sleep.rs similarity index 100% rename from lib/src/fnc/sleep.rs rename to core/src/fnc/sleep.rs diff --git a/lib/src/fnc/string.rs b/core/src/fnc/string.rs similarity index 100% rename from lib/src/fnc/string.rs rename to core/src/fnc/string.rs diff --git a/lib/src/fnc/time.rs b/core/src/fnc/time.rs similarity index 100% rename from lib/src/fnc/time.rs rename to core/src/fnc/time.rs diff --git a/lib/src/fnc/type.rs b/core/src/fnc/type.rs similarity index 100% rename from lib/src/fnc/type.rs rename to core/src/fnc/type.rs diff --git a/lib/src/fnc/util/geo/mod.rs b/core/src/fnc/util/geo/mod.rs similarity index 100% rename from lib/src/fnc/util/geo/mod.rs rename to core/src/fnc/util/geo/mod.rs diff --git a/lib/src/fnc/util/http/mod.rs b/core/src/fnc/util/http/mod.rs similarity index 100% rename from lib/src/fnc/util/http/mod.rs rename to core/src/fnc/util/http/mod.rs diff --git a/lib/src/fnc/util/math/bottom.rs b/core/src/fnc/util/math/bottom.rs similarity index 100% rename from lib/src/fnc/util/math/bottom.rs rename to core/src/fnc/util/math/bottom.rs diff --git a/lib/src/fnc/util/math/deviation.rs b/core/src/fnc/util/math/deviation.rs similarity index 100% rename from lib/src/fnc/util/math/deviation.rs rename to core/src/fnc/util/math/deviation.rs diff --git a/lib/src/fnc/util/math/interquartile.rs b/core/src/fnc/util/math/interquartile.rs similarity index 100% rename from lib/src/fnc/util/math/interquartile.rs rename to core/src/fnc/util/math/interquartile.rs diff --git a/lib/src/fnc/util/math/mean.rs b/core/src/fnc/util/math/mean.rs similarity index 100% rename from lib/src/fnc/util/math/mean.rs rename to core/src/fnc/util/math/mean.rs diff --git a/lib/src/fnc/util/math/median.rs b/core/src/fnc/util/math/median.rs similarity index 100% rename from lib/src/fnc/util/math/median.rs rename to core/src/fnc/util/math/median.rs diff --git a/lib/src/fnc/util/math/midhinge.rs b/core/src/fnc/util/math/midhinge.rs similarity index 100% rename from lib/src/fnc/util/math/midhinge.rs rename to core/src/fnc/util/math/midhinge.rs diff --git a/lib/src/fnc/util/math/mod.rs b/core/src/fnc/util/math/mod.rs similarity index 100% rename from lib/src/fnc/util/math/mod.rs rename to core/src/fnc/util/math/mod.rs diff --git a/lib/src/fnc/util/math/mode.rs b/core/src/fnc/util/math/mode.rs similarity index 100% rename from lib/src/fnc/util/math/mode.rs rename to core/src/fnc/util/math/mode.rs diff --git a/lib/src/fnc/util/math/nearestrank.rs b/core/src/fnc/util/math/nearestrank.rs similarity index 100% rename from lib/src/fnc/util/math/nearestrank.rs rename to core/src/fnc/util/math/nearestrank.rs diff --git a/lib/src/fnc/util/math/percentile.rs b/core/src/fnc/util/math/percentile.rs similarity index 100% rename from lib/src/fnc/util/math/percentile.rs rename to core/src/fnc/util/math/percentile.rs diff --git a/lib/src/fnc/util/math/quartile.rs b/core/src/fnc/util/math/quartile.rs similarity index 100% rename from lib/src/fnc/util/math/quartile.rs rename to core/src/fnc/util/math/quartile.rs diff --git a/lib/src/fnc/util/math/spread.rs b/core/src/fnc/util/math/spread.rs similarity index 100% rename from lib/src/fnc/util/math/spread.rs rename to core/src/fnc/util/math/spread.rs diff --git a/lib/src/fnc/util/math/top.rs b/core/src/fnc/util/math/top.rs similarity index 100% rename from lib/src/fnc/util/math/top.rs rename to core/src/fnc/util/math/top.rs diff --git a/lib/src/fnc/util/math/trimean.rs b/core/src/fnc/util/math/trimean.rs similarity index 100% rename from lib/src/fnc/util/math/trimean.rs rename to core/src/fnc/util/math/trimean.rs diff --git a/lib/src/fnc/util/math/variance.rs b/core/src/fnc/util/math/variance.rs similarity index 100% rename from lib/src/fnc/util/math/variance.rs rename to core/src/fnc/util/math/variance.rs diff --git a/lib/src/fnc/util/math/vector.rs b/core/src/fnc/util/math/vector.rs similarity index 100% rename from lib/src/fnc/util/math/vector.rs rename to core/src/fnc/util/math/vector.rs diff --git a/lib/src/fnc/util/mod.rs b/core/src/fnc/util/mod.rs similarity index 100% rename from lib/src/fnc/util/mod.rs rename to core/src/fnc/util/mod.rs diff --git a/lib/src/fnc/util/string/fuzzy.rs b/core/src/fnc/util/string/fuzzy.rs similarity index 100% rename from lib/src/fnc/util/string/fuzzy.rs rename to core/src/fnc/util/string/fuzzy.rs diff --git a/lib/src/fnc/util/string/mod.rs b/core/src/fnc/util/string/mod.rs similarity index 100% rename from lib/src/fnc/util/string/mod.rs rename to core/src/fnc/util/string/mod.rs diff --git a/lib/src/fnc/util/string/slug.rs b/core/src/fnc/util/string/slug.rs similarity index 100% rename from lib/src/fnc/util/string/slug.rs rename to core/src/fnc/util/string/slug.rs diff --git a/lib/src/fnc/vector.rs b/core/src/fnc/vector.rs similarity index 100% rename from lib/src/fnc/vector.rs rename to core/src/fnc/vector.rs diff --git a/lib/src/iam/auth.rs b/core/src/iam/auth.rs similarity index 100% rename from lib/src/iam/auth.rs rename to core/src/iam/auth.rs diff --git a/lib/src/iam/base.rs b/core/src/iam/base.rs similarity index 100% rename from lib/src/iam/base.rs rename to core/src/iam/base.rs diff --git a/lib/src/iam/check.rs b/core/src/iam/check.rs similarity index 100% rename from lib/src/iam/check.rs rename to core/src/iam/check.rs diff --git a/lib/src/iam/clear.rs b/core/src/iam/clear.rs similarity index 100% rename from lib/src/iam/clear.rs rename to core/src/iam/clear.rs diff --git a/lib/src/iam/entities/action.rs b/core/src/iam/entities/action.rs similarity index 100% rename from lib/src/iam/entities/action.rs rename to core/src/iam/entities/action.rs diff --git a/lib/src/iam/entities/mod.rs b/core/src/iam/entities/mod.rs similarity index 100% rename from lib/src/iam/entities/mod.rs rename to core/src/iam/entities/mod.rs diff --git a/lib/src/iam/entities/resources/actor.rs b/core/src/iam/entities/resources/actor.rs similarity index 100% rename from lib/src/iam/entities/resources/actor.rs rename to core/src/iam/entities/resources/actor.rs diff --git a/lib/src/iam/entities/resources/level.rs b/core/src/iam/entities/resources/level.rs similarity index 100% rename from lib/src/iam/entities/resources/level.rs rename to core/src/iam/entities/resources/level.rs diff --git a/lib/src/iam/entities/resources/mod.rs b/core/src/iam/entities/resources/mod.rs similarity index 100% rename from lib/src/iam/entities/resources/mod.rs rename to core/src/iam/entities/resources/mod.rs diff --git a/lib/src/iam/entities/resources/resource.rs b/core/src/iam/entities/resources/resource.rs similarity index 100% rename from lib/src/iam/entities/resources/resource.rs rename to core/src/iam/entities/resources/resource.rs diff --git a/lib/src/iam/entities/roles.rs b/core/src/iam/entities/roles.rs similarity index 100% rename from lib/src/iam/entities/roles.rs rename to core/src/iam/entities/roles.rs diff --git a/lib/src/iam/entities/schema.rs b/core/src/iam/entities/schema.rs similarity index 100% rename from lib/src/iam/entities/schema.rs rename to core/src/iam/entities/schema.rs diff --git a/lib/src/iam/jwks.rs b/core/src/iam/jwks.rs similarity index 99% rename from lib/src/iam/jwks.rs rename to core/src/iam/jwks.rs index 1ac4f346..0fd12803 100644 --- a/lib/src/iam/jwks.rs +++ b/core/src/iam/jwks.rs @@ -1,6 +1,6 @@ +use crate::dbs::capabilities::NetTarget; use crate::err::Error; use crate::kvs::Datastore; -use crate::opt::capabilities::NetTarget; use chrono::{DateTime, Duration, Utc}; use jsonwebtoken::jwk::{Jwk, JwkSet, KeyOperations, PublicKeyUse}; use jsonwebtoken::{DecodingKey, Validation}; @@ -42,6 +42,7 @@ static CACHE_COOLDOWN: Lazy = } }); +#[cfg(not(target_arch = "wasm32"))] static REMOTE_TIMEOUT: Lazy = Lazy::new(|| match std::env::var("SURREAL_JWKS_REMOTE_TIMEOUT_MILLISECONDS") { Ok(milliseconds_str) => { @@ -278,7 +279,7 @@ fn cache_path_from_url(url: &str) -> String { #[cfg(test)] mod tests { use super::*; - use crate::opt::capabilities::{Capabilities, NetTarget, Targets}; + use crate::dbs::capabilities::{Capabilities, NetTarget, Targets}; use rand::{distributions::Alphanumeric, Rng}; use wiremock::matchers::{method, path}; use wiremock::{Mock, MockServer, ResponseTemplate}; @@ -650,6 +651,7 @@ mod tests { } #[tokio::test] + #[cfg(not(target_arch = "wasm32"))] async fn test_remote_timeout() { let ds = Datastore::new("memory").await.unwrap().with_capabilities( Capabilities::default().with_network_targets(Targets::::Some( diff --git a/lib/src/iam/mod.rs b/core/src/iam/mod.rs similarity index 100% rename from lib/src/iam/mod.rs rename to core/src/iam/mod.rs diff --git a/lib/src/iam/policies/mod.rs b/core/src/iam/policies/mod.rs similarity index 100% rename from lib/src/iam/policies/mod.rs rename to core/src/iam/policies/mod.rs diff --git a/lib/src/iam/policies/policy_set.rs b/core/src/iam/policies/policy_set.rs similarity index 100% rename from lib/src/iam/policies/policy_set.rs rename to core/src/iam/policies/policy_set.rs diff --git a/lib/src/iam/signin.rs b/core/src/iam/signin.rs similarity index 100% rename from lib/src/iam/signin.rs rename to core/src/iam/signin.rs diff --git a/lib/src/iam/signup.rs b/core/src/iam/signup.rs similarity index 100% rename from lib/src/iam/signup.rs rename to core/src/iam/signup.rs diff --git a/lib/src/iam/token.rs b/core/src/iam/token.rs similarity index 100% rename from lib/src/iam/token.rs rename to core/src/iam/token.rs diff --git a/lib/src/iam/verify.rs b/core/src/iam/verify.rs similarity index 99% rename from lib/src/iam/verify.rs rename to core/src/iam/verify.rs index 97dbd708..7e00d7a9 100644 --- a/lib/src/iam/verify.rs +++ b/core/src/iam/verify.rs @@ -1356,7 +1356,7 @@ mod tests { #[cfg(feature = "jwks")] #[tokio::test] async fn test_token_scope_jwks() { - use crate::opt::capabilities::{Capabilities, NetTarget, Targets}; + use crate::dbs::capabilities::{Capabilities, NetTarget, Targets}; use base64_lib::{engine::general_purpose::STANDARD_NO_PAD, Engine}; use jsonwebtoken::jwk::{Jwk, JwkSet}; use rand::{distributions::Alphanumeric, Rng}; diff --git a/lib/src/idg/mod.rs b/core/src/idg/mod.rs similarity index 100% rename from lib/src/idg/mod.rs rename to core/src/idg/mod.rs diff --git a/lib/src/idg/u32.rs b/core/src/idg/u32.rs similarity index 100% rename from lib/src/idg/u32.rs rename to core/src/idg/u32.rs diff --git a/lib/src/idx/docids.rs b/core/src/idx/docids.rs similarity index 100% rename from lib/src/idx/docids.rs rename to core/src/idx/docids.rs diff --git a/lib/src/idx/ft/analyzer/filter.rs b/core/src/idx/ft/analyzer/filter.rs similarity index 100% rename from lib/src/idx/ft/analyzer/filter.rs rename to core/src/idx/ft/analyzer/filter.rs diff --git a/lib/src/idx/ft/analyzer/mod.rs b/core/src/idx/ft/analyzer/mod.rs similarity index 100% rename from lib/src/idx/ft/analyzer/mod.rs rename to core/src/idx/ft/analyzer/mod.rs diff --git a/lib/src/idx/ft/analyzer/tokenizer.rs b/core/src/idx/ft/analyzer/tokenizer.rs similarity index 100% rename from lib/src/idx/ft/analyzer/tokenizer.rs rename to core/src/idx/ft/analyzer/tokenizer.rs diff --git a/lib/src/idx/ft/doclength.rs b/core/src/idx/ft/doclength.rs similarity index 100% rename from lib/src/idx/ft/doclength.rs rename to core/src/idx/ft/doclength.rs diff --git a/lib/src/idx/ft/highlighter.rs b/core/src/idx/ft/highlighter.rs similarity index 100% rename from lib/src/idx/ft/highlighter.rs rename to core/src/idx/ft/highlighter.rs diff --git a/lib/src/idx/ft/mod.rs b/core/src/idx/ft/mod.rs similarity index 100% rename from lib/src/idx/ft/mod.rs rename to core/src/idx/ft/mod.rs diff --git a/lib/src/idx/ft/offsets.rs b/core/src/idx/ft/offsets.rs similarity index 100% rename from lib/src/idx/ft/offsets.rs rename to core/src/idx/ft/offsets.rs diff --git a/lib/src/idx/ft/postings.rs b/core/src/idx/ft/postings.rs similarity index 100% rename from lib/src/idx/ft/postings.rs rename to core/src/idx/ft/postings.rs diff --git a/lib/src/idx/ft/scorer.rs b/core/src/idx/ft/scorer.rs similarity index 100% rename from lib/src/idx/ft/scorer.rs rename to core/src/idx/ft/scorer.rs diff --git a/lib/src/idx/ft/termdocs.rs b/core/src/idx/ft/termdocs.rs similarity index 100% rename from lib/src/idx/ft/termdocs.rs rename to core/src/idx/ft/termdocs.rs diff --git a/lib/src/idx/ft/terms.rs b/core/src/idx/ft/terms.rs similarity index 100% rename from lib/src/idx/ft/terms.rs rename to core/src/idx/ft/terms.rs diff --git a/lib/src/idx/mod.rs b/core/src/idx/mod.rs similarity index 100% rename from lib/src/idx/mod.rs rename to core/src/idx/mod.rs diff --git a/lib/src/idx/planner/executor.rs b/core/src/idx/planner/executor.rs similarity index 100% rename from lib/src/idx/planner/executor.rs rename to core/src/idx/planner/executor.rs diff --git a/lib/src/idx/planner/iterators.rs b/core/src/idx/planner/iterators.rs similarity index 100% rename from lib/src/idx/planner/iterators.rs rename to core/src/idx/planner/iterators.rs diff --git a/lib/src/idx/planner/knn.rs b/core/src/idx/planner/knn.rs similarity index 100% rename from lib/src/idx/planner/knn.rs rename to core/src/idx/planner/knn.rs diff --git a/lib/src/idx/planner/mod.rs b/core/src/idx/planner/mod.rs similarity index 100% rename from lib/src/idx/planner/mod.rs rename to core/src/idx/planner/mod.rs diff --git a/lib/src/idx/planner/plan.rs b/core/src/idx/planner/plan.rs similarity index 100% rename from lib/src/idx/planner/plan.rs rename to core/src/idx/planner/plan.rs diff --git a/lib/src/idx/planner/tree.rs b/core/src/idx/planner/tree.rs similarity index 100% rename from lib/src/idx/planner/tree.rs rename to core/src/idx/planner/tree.rs diff --git a/lib/src/idx/trees/bkeys.rs b/core/src/idx/trees/bkeys.rs similarity index 100% rename from lib/src/idx/trees/bkeys.rs rename to core/src/idx/trees/bkeys.rs diff --git a/lib/src/idx/trees/btree.rs b/core/src/idx/trees/btree.rs similarity index 100% rename from lib/src/idx/trees/btree.rs rename to core/src/idx/trees/btree.rs diff --git a/lib/src/idx/trees/mod.rs b/core/src/idx/trees/mod.rs similarity index 100% rename from lib/src/idx/trees/mod.rs rename to core/src/idx/trees/mod.rs diff --git a/lib/src/idx/trees/mtree.rs b/core/src/idx/trees/mtree.rs similarity index 100% rename from lib/src/idx/trees/mtree.rs rename to core/src/idx/trees/mtree.rs diff --git a/lib/src/idx/trees/store/cache.rs b/core/src/idx/trees/store/cache.rs similarity index 100% rename from lib/src/idx/trees/store/cache.rs rename to core/src/idx/trees/store/cache.rs diff --git a/lib/src/idx/trees/store/mod.rs b/core/src/idx/trees/store/mod.rs similarity index 100% rename from lib/src/idx/trees/store/mod.rs rename to core/src/idx/trees/store/mod.rs diff --git a/lib/src/idx/trees/store/tree.rs b/core/src/idx/trees/store/tree.rs similarity index 100% rename from lib/src/idx/trees/store/tree.rs rename to core/src/idx/trees/store/tree.rs diff --git a/lib/src/idx/trees/vector.rs b/core/src/idx/trees/vector.rs similarity index 100% rename from lib/src/idx/trees/vector.rs rename to core/src/idx/trees/vector.rs diff --git a/lib/src/key/change/mod.rs b/core/src/key/change/mod.rs similarity index 100% rename from lib/src/key/change/mod.rs rename to core/src/key/change/mod.rs diff --git a/lib/src/key/database/all.rs b/core/src/key/database/all.rs similarity index 100% rename from lib/src/key/database/all.rs rename to core/src/key/database/all.rs diff --git a/lib/src/key/database/az.rs b/core/src/key/database/az.rs similarity index 100% rename from lib/src/key/database/az.rs rename to core/src/key/database/az.rs diff --git a/lib/src/key/database/fc.rs b/core/src/key/database/fc.rs similarity index 100% rename from lib/src/key/database/fc.rs rename to core/src/key/database/fc.rs diff --git a/lib/src/key/database/ml.rs b/core/src/key/database/ml.rs similarity index 100% rename from lib/src/key/database/ml.rs rename to core/src/key/database/ml.rs diff --git a/lib/src/key/database/mod.rs b/core/src/key/database/mod.rs similarity index 100% rename from lib/src/key/database/mod.rs rename to core/src/key/database/mod.rs diff --git a/lib/src/key/database/pa.rs b/core/src/key/database/pa.rs similarity index 100% rename from lib/src/key/database/pa.rs rename to core/src/key/database/pa.rs diff --git a/lib/src/key/database/sc.rs b/core/src/key/database/sc.rs similarity index 100% rename from lib/src/key/database/sc.rs rename to core/src/key/database/sc.rs diff --git a/lib/src/key/database/tb.rs b/core/src/key/database/tb.rs similarity index 100% rename from lib/src/key/database/tb.rs rename to core/src/key/database/tb.rs diff --git a/lib/src/key/database/ti.rs b/core/src/key/database/ti.rs similarity index 100% rename from lib/src/key/database/ti.rs rename to core/src/key/database/ti.rs diff --git a/lib/src/key/database/tk.rs b/core/src/key/database/tk.rs similarity index 100% rename from lib/src/key/database/tk.rs rename to core/src/key/database/tk.rs diff --git a/lib/src/key/database/ts.rs b/core/src/key/database/ts.rs similarity index 100% rename from lib/src/key/database/ts.rs rename to core/src/key/database/ts.rs diff --git a/lib/src/key/database/us.rs b/core/src/key/database/us.rs similarity index 100% rename from lib/src/key/database/us.rs rename to core/src/key/database/us.rs diff --git a/lib/src/key/database/vs.rs b/core/src/key/database/vs.rs similarity index 100% rename from lib/src/key/database/vs.rs rename to core/src/key/database/vs.rs diff --git a/lib/src/key/debug.rs b/core/src/key/debug.rs similarity index 100% rename from lib/src/key/debug.rs rename to core/src/key/debug.rs diff --git a/lib/src/key/error.rs b/core/src/key/error.rs similarity index 100% rename from lib/src/key/error.rs rename to core/src/key/error.rs diff --git a/lib/src/key/graph/mod.rs b/core/src/key/graph/mod.rs similarity index 100% rename from lib/src/key/graph/mod.rs rename to core/src/key/graph/mod.rs diff --git a/lib/src/key/index/all.rs b/core/src/key/index/all.rs similarity index 100% rename from lib/src/key/index/all.rs rename to core/src/key/index/all.rs diff --git a/lib/src/key/index/bc.rs b/core/src/key/index/bc.rs similarity index 100% rename from lib/src/key/index/bc.rs rename to core/src/key/index/bc.rs diff --git a/lib/src/key/index/bd.rs b/core/src/key/index/bd.rs similarity index 100% rename from lib/src/key/index/bd.rs rename to core/src/key/index/bd.rs diff --git a/lib/src/key/index/bf.rs b/core/src/key/index/bf.rs similarity index 100% rename from lib/src/key/index/bf.rs rename to core/src/key/index/bf.rs diff --git a/lib/src/key/index/bi.rs b/core/src/key/index/bi.rs similarity index 100% rename from lib/src/key/index/bi.rs rename to core/src/key/index/bi.rs diff --git a/lib/src/key/index/bk.rs b/core/src/key/index/bk.rs similarity index 100% rename from lib/src/key/index/bk.rs rename to core/src/key/index/bk.rs diff --git a/lib/src/key/index/bl.rs b/core/src/key/index/bl.rs similarity index 100% rename from lib/src/key/index/bl.rs rename to core/src/key/index/bl.rs diff --git a/lib/src/key/index/bo.rs b/core/src/key/index/bo.rs similarity index 100% rename from lib/src/key/index/bo.rs rename to core/src/key/index/bo.rs diff --git a/lib/src/key/index/bp.rs b/core/src/key/index/bp.rs similarity index 100% rename from lib/src/key/index/bp.rs rename to core/src/key/index/bp.rs diff --git a/lib/src/key/index/bs.rs b/core/src/key/index/bs.rs similarity index 100% rename from lib/src/key/index/bs.rs rename to core/src/key/index/bs.rs diff --git a/lib/src/key/index/bt.rs b/core/src/key/index/bt.rs similarity index 100% rename from lib/src/key/index/bt.rs rename to core/src/key/index/bt.rs diff --git a/lib/src/key/index/bu.rs b/core/src/key/index/bu.rs similarity index 100% rename from lib/src/key/index/bu.rs rename to core/src/key/index/bu.rs diff --git a/lib/src/key/index/mod.rs b/core/src/key/index/mod.rs similarity index 100% rename from lib/src/key/index/mod.rs rename to core/src/key/index/mod.rs diff --git a/lib/src/key/index/vm.rs b/core/src/key/index/vm.rs similarity index 100% rename from lib/src/key/index/vm.rs rename to core/src/key/index/vm.rs diff --git a/lib/src/key/key_req.rs b/core/src/key/key_req.rs similarity index 100% rename from lib/src/key/key_req.rs rename to core/src/key/key_req.rs diff --git a/lib/src/key/mod.rs b/core/src/key/mod.rs similarity index 100% rename from lib/src/key/mod.rs rename to core/src/key/mod.rs diff --git a/lib/src/key/namespace/all.rs b/core/src/key/namespace/all.rs similarity index 100% rename from lib/src/key/namespace/all.rs rename to core/src/key/namespace/all.rs diff --git a/lib/src/key/namespace/db.rs b/core/src/key/namespace/db.rs similarity index 100% rename from lib/src/key/namespace/db.rs rename to core/src/key/namespace/db.rs diff --git a/lib/src/key/namespace/di.rs b/core/src/key/namespace/di.rs similarity index 100% rename from lib/src/key/namespace/di.rs rename to core/src/key/namespace/di.rs diff --git a/lib/src/key/namespace/mod.rs b/core/src/key/namespace/mod.rs similarity index 100% rename from lib/src/key/namespace/mod.rs rename to core/src/key/namespace/mod.rs diff --git a/lib/src/key/namespace/tk.rs b/core/src/key/namespace/tk.rs similarity index 100% rename from lib/src/key/namespace/tk.rs rename to core/src/key/namespace/tk.rs diff --git a/lib/src/key/namespace/us.rs b/core/src/key/namespace/us.rs similarity index 100% rename from lib/src/key/namespace/us.rs rename to core/src/key/namespace/us.rs diff --git a/lib/src/key/node/all.rs b/core/src/key/node/all.rs similarity index 100% rename from lib/src/key/node/all.rs rename to core/src/key/node/all.rs diff --git a/lib/src/key/node/lq.rs b/core/src/key/node/lq.rs similarity index 100% rename from lib/src/key/node/lq.rs rename to core/src/key/node/lq.rs diff --git a/lib/src/key/node/mod.rs b/core/src/key/node/mod.rs similarity index 100% rename from lib/src/key/node/mod.rs rename to core/src/key/node/mod.rs diff --git a/lib/src/key/root/all.rs b/core/src/key/root/all.rs similarity index 100% rename from lib/src/key/root/all.rs rename to core/src/key/root/all.rs diff --git a/lib/src/key/root/hb.rs b/core/src/key/root/hb.rs similarity index 100% rename from lib/src/key/root/hb.rs rename to core/src/key/root/hb.rs diff --git a/lib/src/key/root/mod.rs b/core/src/key/root/mod.rs similarity index 100% rename from lib/src/key/root/mod.rs rename to core/src/key/root/mod.rs diff --git a/lib/src/key/root/nd.rs b/core/src/key/root/nd.rs similarity index 100% rename from lib/src/key/root/nd.rs rename to core/src/key/root/nd.rs diff --git a/lib/src/key/root/ni.rs b/core/src/key/root/ni.rs similarity index 100% rename from lib/src/key/root/ni.rs rename to core/src/key/root/ni.rs diff --git a/lib/src/key/root/ns.rs b/core/src/key/root/ns.rs similarity index 100% rename from lib/src/key/root/ns.rs rename to core/src/key/root/ns.rs diff --git a/lib/src/key/root/us.rs b/core/src/key/root/us.rs similarity index 100% rename from lib/src/key/root/us.rs rename to core/src/key/root/us.rs diff --git a/lib/src/key/scope/all.rs b/core/src/key/scope/all.rs similarity index 100% rename from lib/src/key/scope/all.rs rename to core/src/key/scope/all.rs diff --git a/lib/src/key/scope/mod.rs b/core/src/key/scope/mod.rs similarity index 100% rename from lib/src/key/scope/mod.rs rename to core/src/key/scope/mod.rs diff --git a/lib/src/key/scope/tk.rs b/core/src/key/scope/tk.rs similarity index 100% rename from lib/src/key/scope/tk.rs rename to core/src/key/scope/tk.rs diff --git a/lib/src/key/table/all.rs b/core/src/key/table/all.rs similarity index 100% rename from lib/src/key/table/all.rs rename to core/src/key/table/all.rs diff --git a/lib/src/key/table/ev.rs b/core/src/key/table/ev.rs similarity index 100% rename from lib/src/key/table/ev.rs rename to core/src/key/table/ev.rs diff --git a/lib/src/key/table/fd.rs b/core/src/key/table/fd.rs similarity index 100% rename from lib/src/key/table/fd.rs rename to core/src/key/table/fd.rs diff --git a/lib/src/key/table/ft.rs b/core/src/key/table/ft.rs similarity index 100% rename from lib/src/key/table/ft.rs rename to core/src/key/table/ft.rs diff --git a/lib/src/key/table/ix.rs b/core/src/key/table/ix.rs similarity index 100% rename from lib/src/key/table/ix.rs rename to core/src/key/table/ix.rs diff --git a/lib/src/key/table/lq.rs b/core/src/key/table/lq.rs similarity index 100% rename from lib/src/key/table/lq.rs rename to core/src/key/table/lq.rs diff --git a/lib/src/key/table/mod.rs b/core/src/key/table/mod.rs similarity index 100% rename from lib/src/key/table/mod.rs rename to core/src/key/table/mod.rs diff --git a/lib/src/key/thing/mod.rs b/core/src/key/thing/mod.rs similarity index 100% rename from lib/src/key/thing/mod.rs rename to core/src/key/thing/mod.rs diff --git a/lib/src/kvs/cache.rs b/core/src/kvs/cache.rs similarity index 100% rename from lib/src/kvs/cache.rs rename to core/src/kvs/cache.rs diff --git a/lib/src/kvs/clock.rs b/core/src/kvs/clock.rs similarity index 100% rename from lib/src/kvs/clock.rs rename to core/src/kvs/clock.rs diff --git a/lib/src/kvs/ds.rs b/core/src/kvs/ds.rs similarity index 96% rename from lib/src/kvs/ds.rs rename to core/src/kvs/ds.rs index 2baee038..dcd24693 100644 --- a/lib/src/kvs/ds.rs +++ b/core/src/kvs/ds.rs @@ -1,6 +1,8 @@ use super::tx::Transaction; use crate::cf; use crate::ctx::Context; +#[cfg(feature = "jwks")] +use crate::dbs::capabilities::NetTarget; use crate::dbs::{ node::Timestamp, Attach, Capabilities, Executor, Notification, Options, Response, Session, Variables, @@ -13,9 +15,6 @@ use crate::kvs::clock::SizedClock; #[allow(unused_imports)] use crate::kvs::clock::SystemClock; use crate::kvs::{LockType, LockType::*, TransactionType, TransactionType::*}; -use crate::opt::auth::Root; -#[cfg(feature = "jwks")] -use crate::opt::capabilities::NetTarget; use crate::sql::{self, statements::DefineUserStatement, Base, Query, Uuid, Value}; use crate::syn; use crate::vs::Oracle; @@ -165,8 +164,8 @@ impl Datastore { /// # Examples /// /// ```rust,no_run - /// # use surrealdb::kvs::Datastore; - /// # use surrealdb::err::Error; + /// # use surrealdb_core::kvs::Datastore; + /// # use surrealdb_core::err::Error; /// # #[tokio::main] /// # async fn main() -> Result<(), Error> { /// let ds = Datastore::new("memory").await?; @@ -177,8 +176,8 @@ impl Datastore { /// Or to create a file-backed store: /// /// ```rust,no_run - /// # use surrealdb::kvs::Datastore; - /// # use surrealdb::err::Error; + /// # use surrealdb_core::kvs::Datastore; + /// # use surrealdb_core::err::Error; /// # #[tokio::main] /// # async fn main() -> Result<(), Error> { /// let ds = Datastore::new("file://temp.db").await?; @@ -189,8 +188,8 @@ impl Datastore { /// Or to connect to a tikv-backed distributed store: /// /// ```rust,no_run - /// # use surrealdb::kvs::Datastore; - /// # use surrealdb::err::Error; + /// # use surrealdb_core::kvs::Datastore; + /// # use surrealdb_core::err::Error; /// # #[tokio::main] /// # async fn main() -> Result<(), Error> { /// let ds = Datastore::new("tikv://127.0.0.1:2379").await?; @@ -440,7 +439,7 @@ impl Datastore { /// Trigger the `unreachable definition` compilation error, probably due to this issue: /// https://github.com/rust-lang/rust/issues/111370 #[allow(unreachable_code, unused_variables)] - pub async fn setup_initial_creds(&self, creds: Root<'_>) -> Result<(), Error> { + pub async fn setup_initial_creds(&self, username: &str, password: &str) -> Result<(), Error> { // Start a new writeable transaction let txn = self.transaction(Write, Optimistic).await?.rollback_with_panic().enclose(); // Fetch the root users from the storage @@ -449,9 +448,9 @@ impl Datastore { match users { Ok(v) if v.is_empty() => { // Display information in the logs - info!("Credentials were provided, and no root users were found. The root user '{}' will be created", creds.username); + info!("Credentials were provided, and no root users were found. The root user '{}' will be created", username); // Create and save a new root users - let stm = DefineUserStatement::from((Base::Root, creds.username, creds.password)); + let stm = DefineUserStatement::from((Base::Root, username, password)); let ctx = Context::default(); let opt = Options::new().with_auth(Arc::new(Auth::for_root(Role::Owner))); let _ = stm.compute(&ctx, &opt, &txn, None).await?; @@ -462,7 +461,7 @@ impl Datastore { } Ok(_) => { // Display warnings in the logs - warn!("Credentials were provided, but existing root users were found. The root user '{}' will not be created", creds.username); + warn!("Credentials were provided, but existing root users were found. The root user '{}' will not be created", username); warn!("Consider removing the --user and --pass arguments from the server start command"); // We didn't write anything, so just rollback txn.lock().await.cancel().await?; @@ -921,8 +920,8 @@ impl Datastore { /// Create a new transaction on this datastore /// /// ```rust,no_run - /// use surrealdb::kvs::{Datastore, TransactionType::*, LockType::*}; - /// use surrealdb::err::Error; + /// use surrealdb_core::kvs::{Datastore, TransactionType::*, LockType::*}; + /// use surrealdb_core::err::Error; /// /// #[tokio::main] /// async fn main() -> Result<(), Error> { @@ -996,9 +995,9 @@ impl Datastore { /// Parse and execute an SQL query /// /// ```rust,no_run - /// use surrealdb::kvs::Datastore; - /// use surrealdb::err::Error; - /// use surrealdb::dbs::Session; + /// use surrealdb_core::kvs::Datastore; + /// use surrealdb_core::err::Error; + /// use surrealdb_core::dbs::Session; /// /// #[tokio::main] /// async fn main() -> Result<(), Error> { @@ -1025,10 +1024,10 @@ impl Datastore { /// Execute a pre-parsed SQL query /// /// ```rust,no_run - /// use surrealdb::kvs::Datastore; - /// use surrealdb::err::Error; - /// use surrealdb::dbs::Session; - /// use surrealdb::sql::parse; + /// use surrealdb_core::kvs::Datastore; + /// use surrealdb_core::err::Error; + /// use surrealdb_core::dbs::Session; + /// use surrealdb_core::sql::parse; /// /// #[tokio::main] /// async fn main() -> Result<(), Error> { @@ -1088,11 +1087,11 @@ impl Datastore { /// Ensure a SQL [`Value`] is fully computed /// /// ```rust,no_run - /// use surrealdb::kvs::Datastore; - /// use surrealdb::err::Error; - /// use surrealdb::dbs::Session; - /// use surrealdb::sql::Future; - /// use surrealdb::sql::Value; + /// use surrealdb_core::kvs::Datastore; + /// use surrealdb_core::err::Error; + /// use surrealdb_core::dbs::Session; + /// use surrealdb_core::sql::Future; + /// use surrealdb_core::sql::Value; /// /// #[tokio::main] /// async fn main() -> Result<(), Error> { @@ -1167,11 +1166,11 @@ impl Datastore { /// SIGNIN clause, which still needs to work without guest access. /// /// ```rust,no_run - /// use surrealdb::kvs::Datastore; - /// use surrealdb::err::Error; - /// use surrealdb::dbs::Session; - /// use surrealdb::sql::Future; - /// use surrealdb::sql::Value; + /// use surrealdb_core::kvs::Datastore; + /// use surrealdb_core::err::Error; + /// use surrealdb_core::dbs::Session; + /// use surrealdb_core::sql::Future; + /// use surrealdb_core::sql::Value; /// /// #[tokio::main] /// async fn main() -> Result<(), Error> { @@ -1232,9 +1231,9 @@ impl Datastore { /// Subscribe to live notifications /// /// ```rust,no_run - /// use surrealdb::kvs::Datastore; - /// use surrealdb::err::Error; - /// use surrealdb::dbs::Session; + /// use surrealdb_core::kvs::Datastore; + /// use surrealdb_core::err::Error; + /// use surrealdb_core::dbs::Session; /// /// #[tokio::main] /// async fn main() -> Result<(), Error> { diff --git a/lib/src/kvs/fdb/mod.rs b/core/src/kvs/fdb/mod.rs similarity index 100% rename from lib/src/kvs/fdb/mod.rs rename to core/src/kvs/fdb/mod.rs diff --git a/lib/src/kvs/indxdb/mod.rs b/core/src/kvs/indxdb/mod.rs similarity index 100% rename from lib/src/kvs/indxdb/mod.rs rename to core/src/kvs/indxdb/mod.rs diff --git a/lib/src/kvs/kv.rs b/core/src/kvs/kv.rs similarity index 100% rename from lib/src/kvs/kv.rs rename to core/src/kvs/kv.rs diff --git a/lib/src/kvs/mem/mod.rs b/core/src/kvs/mem/mod.rs similarity index 100% rename from lib/src/kvs/mem/mod.rs rename to core/src/kvs/mem/mod.rs diff --git a/lib/src/kvs/mod.rs b/core/src/kvs/mod.rs similarity index 100% rename from lib/src/kvs/mod.rs rename to core/src/kvs/mod.rs diff --git a/lib/src/kvs/rocksdb/cnf.rs b/core/src/kvs/rocksdb/cnf.rs similarity index 100% rename from lib/src/kvs/rocksdb/cnf.rs rename to core/src/kvs/rocksdb/cnf.rs diff --git a/lib/src/kvs/rocksdb/mod.rs b/core/src/kvs/rocksdb/mod.rs similarity index 100% rename from lib/src/kvs/rocksdb/mod.rs rename to core/src/kvs/rocksdb/mod.rs diff --git a/lib/src/kvs/speedb/cnf.rs b/core/src/kvs/speedb/cnf.rs similarity index 100% rename from lib/src/kvs/speedb/cnf.rs rename to core/src/kvs/speedb/cnf.rs diff --git a/lib/src/kvs/speedb/mod.rs b/core/src/kvs/speedb/mod.rs similarity index 100% rename from lib/src/kvs/speedb/mod.rs rename to core/src/kvs/speedb/mod.rs diff --git a/lib/src/kvs/tests/cluster_init.rs b/core/src/kvs/tests/cluster_init.rs similarity index 100% rename from lib/src/kvs/tests/cluster_init.rs rename to core/src/kvs/tests/cluster_init.rs diff --git a/lib/src/kvs/tests/hb.rs b/core/src/kvs/tests/hb.rs similarity index 100% rename from lib/src/kvs/tests/hb.rs rename to core/src/kvs/tests/hb.rs diff --git a/lib/src/kvs/tests/helper.rs b/core/src/kvs/tests/helper.rs similarity index 100% rename from lib/src/kvs/tests/helper.rs rename to core/src/kvs/tests/helper.rs diff --git a/lib/src/kvs/tests/lq.rs b/core/src/kvs/tests/lq.rs similarity index 100% rename from lib/src/kvs/tests/lq.rs rename to core/src/kvs/tests/lq.rs diff --git a/lib/src/kvs/tests/mod.rs b/core/src/kvs/tests/mod.rs similarity index 100% rename from lib/src/kvs/tests/mod.rs rename to core/src/kvs/tests/mod.rs diff --git a/lib/src/kvs/tests/multireader.rs b/core/src/kvs/tests/multireader.rs similarity index 100% rename from lib/src/kvs/tests/multireader.rs rename to core/src/kvs/tests/multireader.rs diff --git a/lib/src/kvs/tests/multiwriter_different_keys.rs b/core/src/kvs/tests/multiwriter_different_keys.rs similarity index 100% rename from lib/src/kvs/tests/multiwriter_different_keys.rs rename to core/src/kvs/tests/multiwriter_different_keys.rs diff --git a/lib/src/kvs/tests/multiwriter_same_keys_allow.rs b/core/src/kvs/tests/multiwriter_same_keys_allow.rs similarity index 100% rename from lib/src/kvs/tests/multiwriter_same_keys_allow.rs rename to core/src/kvs/tests/multiwriter_same_keys_allow.rs diff --git a/lib/src/kvs/tests/multiwriter_same_keys_conflict.rs b/core/src/kvs/tests/multiwriter_same_keys_conflict.rs similarity index 100% rename from lib/src/kvs/tests/multiwriter_same_keys_conflict.rs rename to core/src/kvs/tests/multiwriter_same_keys_conflict.rs diff --git a/lib/src/kvs/tests/nd.rs b/core/src/kvs/tests/nd.rs similarity index 100% rename from lib/src/kvs/tests/nd.rs rename to core/src/kvs/tests/nd.rs diff --git a/lib/src/kvs/tests/ndlq.rs b/core/src/kvs/tests/ndlq.rs similarity index 100% rename from lib/src/kvs/tests/ndlq.rs rename to core/src/kvs/tests/ndlq.rs diff --git a/lib/src/kvs/tests/nq.rs b/core/src/kvs/tests/nq.rs similarity index 100% rename from lib/src/kvs/tests/nq.rs rename to core/src/kvs/tests/nq.rs diff --git a/lib/src/kvs/tests/raw.rs b/core/src/kvs/tests/raw.rs similarity index 100% rename from lib/src/kvs/tests/raw.rs rename to core/src/kvs/tests/raw.rs diff --git a/lib/src/kvs/tests/snapshot.rs b/core/src/kvs/tests/snapshot.rs similarity index 100% rename from lib/src/kvs/tests/snapshot.rs rename to core/src/kvs/tests/snapshot.rs diff --git a/lib/src/kvs/tests/tb.rs b/core/src/kvs/tests/tb.rs similarity index 100% rename from lib/src/kvs/tests/tb.rs rename to core/src/kvs/tests/tb.rs diff --git a/lib/src/kvs/tests/tblq.rs b/core/src/kvs/tests/tblq.rs similarity index 100% rename from lib/src/kvs/tests/tblq.rs rename to core/src/kvs/tests/tblq.rs diff --git a/lib/src/kvs/tests/tbnt.rs b/core/src/kvs/tests/tbnt.rs similarity index 100% rename from lib/src/kvs/tests/tbnt.rs rename to core/src/kvs/tests/tbnt.rs diff --git a/lib/src/kvs/tests/timestamp_to_versionstamp.rs b/core/src/kvs/tests/timestamp_to_versionstamp.rs similarity index 100% rename from lib/src/kvs/tests/timestamp_to_versionstamp.rs rename to core/src/kvs/tests/timestamp_to_versionstamp.rs diff --git a/lib/src/kvs/tikv/mod.rs b/core/src/kvs/tikv/mod.rs similarity index 100% rename from lib/src/kvs/tikv/mod.rs rename to core/src/kvs/tikv/mod.rs diff --git a/lib/src/kvs/tx.rs b/core/src/kvs/tx.rs similarity index 100% rename from lib/src/kvs/tx.rs rename to core/src/kvs/tx.rs diff --git a/core/src/lib.rs b/core/src/lib.rs new file mode 100644 index 00000000..551f4be7 --- /dev/null +++ b/core/src/lib.rs @@ -0,0 +1,53 @@ +#[macro_use] +extern crate tracing; + +#[macro_use] +mod mac; + +mod cf; +mod ctx; +mod doc; +mod exe; +mod fnc; +mod vs; + +pub mod sql; + +#[doc(hidden)] +pub mod cnf; +#[doc(hidden)] +pub mod dbs; +#[doc(hidden)] +pub mod env; +#[doc(hidden)] +pub mod err; +pub(crate) mod fflags; +#[doc(hidden)] +pub mod iam; +#[doc(hidden)] +pub mod idg; +#[doc(hidden)] +pub mod idx; +#[doc(hidden)] +pub mod key; +#[doc(hidden)] +pub mod kvs; +#[cfg(any(feature = "ml", feature = "jwks"))] +#[doc(hidden)] +pub mod obs; +#[doc(hidden)] +pub mod syn; + +#[doc(hidden)] +/// Channels for receiving a SurrealQL database export +pub mod channel { + pub use channel::bounded; + pub use channel::unbounded; + pub use channel::Receiver; + pub use channel::Sender; +} + +#[cfg(feature = "ml")] +#[cfg(not(target_arch = "wasm32"))] +#[doc(hidden)] +pub use surrealml_core as ml; diff --git a/lib/src/mac/mod.rs b/core/src/mac/mod.rs similarity index 86% rename from lib/src/mac/mod.rs rename to core/src/mac/mod.rs index a3ec7c07..cf5982ba 100644 --- a/lib/src/mac/mod.rs +++ b/core/src/mac/mod.rs @@ -1,4 +1,6 @@ /// Converts some text into a new line byte string +#[macro_export] +#[doc(hidden)] macro_rules! bytes { ($expression:expr) => { format!("{}\n", $expression).into_bytes() @@ -6,6 +8,8 @@ macro_rules! bytes { } /// Creates a new b-tree map of key-value pairs +#[macro_export] +#[doc(hidden)] macro_rules! map { ($($k:expr => $v:expr),* $(,)? $( => $x:expr )?) => {{ let mut m = ::std::collections::BTreeMap::new(); @@ -16,6 +20,8 @@ macro_rules! map { } /// Matches on a specific config environment +#[macro_export] +#[doc(hidden)] macro_rules! get_cfg { ($i:ident : $($s:expr),+) => ( let $i = || { $( if cfg!($i=$s) { return $s; } );+ "unknown"}; diff --git a/lib/src/obs/mod.rs b/core/src/obs/mod.rs similarity index 100% rename from lib/src/obs/mod.rs rename to core/src/obs/mod.rs diff --git a/core/src/sql/mod.rs b/core/src/sql/mod.rs new file mode 100644 index 00000000..d10f6214 --- /dev/null +++ b/core/src/sql/mod.rs @@ -0,0 +1,9 @@ +#[cfg(not(feature = "sql2"))] +mod v1; +#[cfg(not(feature = "sql2"))] +pub use v1::*; + +#[cfg(feature = "sql2")] +mod v2; +#[cfg(feature = "sql2")] +pub use v2::*; diff --git a/lib/src/sql/algorithm.rs b/core/src/sql/v1/algorithm.rs similarity index 100% rename from lib/src/sql/algorithm.rs rename to core/src/sql/v1/algorithm.rs diff --git a/lib/src/sql/arbitrary.rs b/core/src/sql/v1/arbitrary.rs similarity index 100% rename from lib/src/sql/arbitrary.rs rename to core/src/sql/v1/arbitrary.rs diff --git a/lib/src/sql/array.rs b/core/src/sql/v1/array.rs similarity index 100% rename from lib/src/sql/array.rs rename to core/src/sql/v1/array.rs diff --git a/lib/src/sql/base.rs b/core/src/sql/v1/base.rs similarity index 100% rename from lib/src/sql/base.rs rename to core/src/sql/v1/base.rs diff --git a/lib/src/sql/block.rs b/core/src/sql/v1/block.rs similarity index 100% rename from lib/src/sql/block.rs rename to core/src/sql/v1/block.rs diff --git a/lib/src/sql/bytes.rs b/core/src/sql/v1/bytes.rs similarity index 100% rename from lib/src/sql/bytes.rs rename to core/src/sql/v1/bytes.rs diff --git a/lib/src/sql/cast.rs b/core/src/sql/v1/cast.rs similarity index 100% rename from lib/src/sql/cast.rs rename to core/src/sql/v1/cast.rs diff --git a/lib/src/sql/changefeed.rs b/core/src/sql/v1/changefeed.rs similarity index 100% rename from lib/src/sql/changefeed.rs rename to core/src/sql/v1/changefeed.rs diff --git a/lib/src/sql/cond.rs b/core/src/sql/v1/cond.rs similarity index 100% rename from lib/src/sql/cond.rs rename to core/src/sql/v1/cond.rs diff --git a/lib/src/sql/constant.rs b/core/src/sql/v1/constant.rs similarity index 100% rename from lib/src/sql/constant.rs rename to core/src/sql/v1/constant.rs diff --git a/lib/src/sql/data.rs b/core/src/sql/v1/data.rs similarity index 100% rename from lib/src/sql/data.rs rename to core/src/sql/v1/data.rs diff --git a/lib/src/sql/datetime.rs b/core/src/sql/v1/datetime.rs similarity index 100% rename from lib/src/sql/datetime.rs rename to core/src/sql/v1/datetime.rs diff --git a/lib/src/sql/dir.rs b/core/src/sql/v1/dir.rs similarity index 100% rename from lib/src/sql/dir.rs rename to core/src/sql/v1/dir.rs diff --git a/lib/src/sql/duration.rs b/core/src/sql/v1/duration.rs similarity index 100% rename from lib/src/sql/duration.rs rename to core/src/sql/v1/duration.rs diff --git a/lib/src/sql/edges.rs b/core/src/sql/v1/edges.rs similarity index 100% rename from lib/src/sql/edges.rs rename to core/src/sql/v1/edges.rs diff --git a/lib/src/sql/ending.rs b/core/src/sql/v1/ending.rs similarity index 100% rename from lib/src/sql/ending.rs rename to core/src/sql/v1/ending.rs diff --git a/lib/src/sql/escape.rs b/core/src/sql/v1/escape.rs similarity index 100% rename from lib/src/sql/escape.rs rename to core/src/sql/v1/escape.rs diff --git a/lib/src/sql/explain.rs b/core/src/sql/v1/explain.rs similarity index 100% rename from lib/src/sql/explain.rs rename to core/src/sql/v1/explain.rs diff --git a/lib/src/sql/expression.rs b/core/src/sql/v1/expression.rs similarity index 98% rename from lib/src/sql/expression.rs rename to core/src/sql/v1/expression.rs index c5ba5ec8..20d5dd31 100644 --- a/lib/src/sql/expression.rs +++ b/core/src/sql/v1/expression.rs @@ -41,7 +41,8 @@ impl Default for Expression { impl Expression { /// Create a new binary expression - pub(crate) fn new(l: Value, o: Operator, r: Value) -> Self { + #[doc(hidden)] + pub fn new(l: Value, o: Operator, r: Value) -> Self { Self::Binary { l, o, diff --git a/lib/src/sql/fetch.rs b/core/src/sql/v1/fetch.rs similarity index 100% rename from lib/src/sql/fetch.rs rename to core/src/sql/v1/fetch.rs diff --git a/lib/src/sql/field.rs b/core/src/sql/v1/field.rs similarity index 100% rename from lib/src/sql/field.rs rename to core/src/sql/v1/field.rs diff --git a/lib/src/sql/filter.rs b/core/src/sql/v1/filter.rs similarity index 100% rename from lib/src/sql/filter.rs rename to core/src/sql/v1/filter.rs diff --git a/lib/src/sql/fmt.rs b/core/src/sql/v1/fmt.rs similarity index 100% rename from lib/src/sql/fmt.rs rename to core/src/sql/v1/fmt.rs diff --git a/lib/src/sql/function.rs b/core/src/sql/v1/function.rs similarity index 100% rename from lib/src/sql/function.rs rename to core/src/sql/v1/function.rs diff --git a/lib/src/sql/future.rs b/core/src/sql/v1/future.rs similarity index 100% rename from lib/src/sql/future.rs rename to core/src/sql/v1/future.rs diff --git a/lib/src/sql/geometry.rs b/core/src/sql/v1/geometry.rs similarity index 100% rename from lib/src/sql/geometry.rs rename to core/src/sql/v1/geometry.rs diff --git a/lib/src/sql/graph.rs b/core/src/sql/v1/graph.rs similarity index 100% rename from lib/src/sql/graph.rs rename to core/src/sql/v1/graph.rs diff --git a/lib/src/sql/group.rs b/core/src/sql/v1/group.rs similarity index 100% rename from lib/src/sql/group.rs rename to core/src/sql/v1/group.rs diff --git a/lib/src/sql/id.rs b/core/src/sql/v1/id.rs similarity index 100% rename from lib/src/sql/id.rs rename to core/src/sql/v1/id.rs diff --git a/lib/src/sql/ident.rs b/core/src/sql/v1/ident.rs similarity index 100% rename from lib/src/sql/ident.rs rename to core/src/sql/v1/ident.rs diff --git a/lib/src/sql/idiom.rs b/core/src/sql/v1/idiom.rs similarity index 100% rename from lib/src/sql/idiom.rs rename to core/src/sql/v1/idiom.rs diff --git a/lib/src/sql/index.rs b/core/src/sql/v1/index.rs similarity index 100% rename from lib/src/sql/index.rs rename to core/src/sql/v1/index.rs diff --git a/lib/src/sql/kind.rs b/core/src/sql/v1/kind.rs similarity index 100% rename from lib/src/sql/kind.rs rename to core/src/sql/v1/kind.rs diff --git a/lib/src/sql/language.rs b/core/src/sql/v1/language.rs similarity index 100% rename from lib/src/sql/language.rs rename to core/src/sql/v1/language.rs diff --git a/lib/src/sql/limit.rs b/core/src/sql/v1/limit.rs similarity index 100% rename from lib/src/sql/limit.rs rename to core/src/sql/v1/limit.rs diff --git a/lib/src/sql/mock.rs b/core/src/sql/v1/mock.rs similarity index 100% rename from lib/src/sql/mock.rs rename to core/src/sql/v1/mock.rs diff --git a/lib/src/sql/mod.rs b/core/src/sql/v1/mod.rs similarity index 96% rename from lib/src/sql/mod.rs rename to core/src/sql/v1/mod.rs index 22c35968..9afc9d36 100644 --- a/lib/src/sql/mod.rs +++ b/core/src/sql/v1/mod.rs @@ -137,6 +137,8 @@ pub use self::timeout::Timeout; pub use self::tokenizer::Tokenizer; pub use self::uuid::Uuid; pub use self::value::serde::to_value; +#[doc(hidden)] +pub use self::value::serde::{from_value, FromValueError}; pub use self::value::Value; pub use self::value::Values; pub use self::version::Version; @@ -149,4 +151,4 @@ mod parser { pub use crate::syn::*; } -pub use self::parser::{error::ParseError, idiom, json, parse, subquery, thing, value}; +pub use self::parser::{idiom, json, parse, subquery, thing, value}; diff --git a/lib/src/sql/model.rs b/core/src/sql/v1/model.rs similarity index 100% rename from lib/src/sql/model.rs rename to core/src/sql/v1/model.rs diff --git a/lib/src/sql/number.rs b/core/src/sql/v1/number.rs similarity index 100% rename from lib/src/sql/number.rs rename to core/src/sql/v1/number.rs diff --git a/lib/src/sql/object.rs b/core/src/sql/v1/object.rs similarity index 100% rename from lib/src/sql/object.rs rename to core/src/sql/v1/object.rs diff --git a/lib/src/sql/operation.rs b/core/src/sql/v1/operation.rs similarity index 100% rename from lib/src/sql/operation.rs rename to core/src/sql/v1/operation.rs diff --git a/lib/src/sql/operator.rs b/core/src/sql/v1/operator.rs similarity index 100% rename from lib/src/sql/operator.rs rename to core/src/sql/v1/operator.rs diff --git a/lib/src/sql/order.rs b/core/src/sql/v1/order.rs similarity index 100% rename from lib/src/sql/order.rs rename to core/src/sql/v1/order.rs diff --git a/lib/src/sql/output.rs b/core/src/sql/v1/output.rs similarity index 100% rename from lib/src/sql/output.rs rename to core/src/sql/v1/output.rs diff --git a/lib/src/sql/param.rs b/core/src/sql/v1/param.rs similarity index 100% rename from lib/src/sql/param.rs rename to core/src/sql/v1/param.rs diff --git a/lib/src/sql/part.rs b/core/src/sql/v1/part.rs similarity index 100% rename from lib/src/sql/part.rs rename to core/src/sql/v1/part.rs diff --git a/lib/src/sql/paths.rs b/core/src/sql/v1/paths.rs similarity index 100% rename from lib/src/sql/paths.rs rename to core/src/sql/v1/paths.rs diff --git a/lib/src/sql/permission.rs b/core/src/sql/v1/permission.rs similarity index 100% rename from lib/src/sql/permission.rs rename to core/src/sql/v1/permission.rs diff --git a/lib/src/sql/query.rs b/core/src/sql/v1/query.rs similarity index 100% rename from lib/src/sql/query.rs rename to core/src/sql/v1/query.rs diff --git a/lib/src/sql/range.rs b/core/src/sql/v1/range.rs similarity index 100% rename from lib/src/sql/range.rs rename to core/src/sql/v1/range.rs diff --git a/lib/src/sql/regex.rs b/core/src/sql/v1/regex.rs similarity index 100% rename from lib/src/sql/regex.rs rename to core/src/sql/v1/regex.rs diff --git a/lib/src/sql/scoring.rs b/core/src/sql/v1/scoring.rs similarity index 100% rename from lib/src/sql/scoring.rs rename to core/src/sql/v1/scoring.rs diff --git a/lib/src/sql/script.rs b/core/src/sql/v1/script.rs similarity index 100% rename from lib/src/sql/script.rs rename to core/src/sql/v1/script.rs diff --git a/lib/src/sql/serde.rs b/core/src/sql/v1/serde.rs similarity index 100% rename from lib/src/sql/serde.rs rename to core/src/sql/v1/serde.rs diff --git a/lib/src/sql/split.rs b/core/src/sql/v1/split.rs similarity index 100% rename from lib/src/sql/split.rs rename to core/src/sql/v1/split.rs diff --git a/lib/src/sql/start.rs b/core/src/sql/v1/start.rs similarity index 100% rename from lib/src/sql/start.rs rename to core/src/sql/v1/start.rs diff --git a/lib/src/sql/statement.rs b/core/src/sql/v1/statement.rs similarity index 100% rename from lib/src/sql/statement.rs rename to core/src/sql/v1/statement.rs diff --git a/lib/src/sql/statements/analyze.rs b/core/src/sql/v1/statements/analyze.rs similarity index 100% rename from lib/src/sql/statements/analyze.rs rename to core/src/sql/v1/statements/analyze.rs diff --git a/lib/src/sql/statements/begin.rs b/core/src/sql/v1/statements/begin.rs similarity index 100% rename from lib/src/sql/statements/begin.rs rename to core/src/sql/v1/statements/begin.rs diff --git a/lib/src/sql/statements/break.rs b/core/src/sql/v1/statements/break.rs similarity index 100% rename from lib/src/sql/statements/break.rs rename to core/src/sql/v1/statements/break.rs diff --git a/lib/src/sql/statements/cancel.rs b/core/src/sql/v1/statements/cancel.rs similarity index 100% rename from lib/src/sql/statements/cancel.rs rename to core/src/sql/v1/statements/cancel.rs diff --git a/lib/src/sql/statements/commit.rs b/core/src/sql/v1/statements/commit.rs similarity index 100% rename from lib/src/sql/statements/commit.rs rename to core/src/sql/v1/statements/commit.rs diff --git a/lib/src/sql/statements/continue.rs b/core/src/sql/v1/statements/continue.rs similarity index 100% rename from lib/src/sql/statements/continue.rs rename to core/src/sql/v1/statements/continue.rs diff --git a/lib/src/sql/statements/create.rs b/core/src/sql/v1/statements/create.rs similarity index 100% rename from lib/src/sql/statements/create.rs rename to core/src/sql/v1/statements/create.rs diff --git a/lib/src/sql/statements/define/analyzer.rs b/core/src/sql/v1/statements/define/analyzer.rs similarity index 100% rename from lib/src/sql/statements/define/analyzer.rs rename to core/src/sql/v1/statements/define/analyzer.rs diff --git a/lib/src/sql/statements/define/database.rs b/core/src/sql/v1/statements/define/database.rs similarity index 100% rename from lib/src/sql/statements/define/database.rs rename to core/src/sql/v1/statements/define/database.rs diff --git a/lib/src/sql/statements/define/event.rs b/core/src/sql/v1/statements/define/event.rs similarity index 100% rename from lib/src/sql/statements/define/event.rs rename to core/src/sql/v1/statements/define/event.rs diff --git a/lib/src/sql/statements/define/field.rs b/core/src/sql/v1/statements/define/field.rs similarity index 100% rename from lib/src/sql/statements/define/field.rs rename to core/src/sql/v1/statements/define/field.rs diff --git a/lib/src/sql/statements/define/function.rs b/core/src/sql/v1/statements/define/function.rs similarity index 100% rename from lib/src/sql/statements/define/function.rs rename to core/src/sql/v1/statements/define/function.rs diff --git a/lib/src/sql/statements/define/index.rs b/core/src/sql/v1/statements/define/index.rs similarity index 100% rename from lib/src/sql/statements/define/index.rs rename to core/src/sql/v1/statements/define/index.rs diff --git a/lib/src/sql/statements/define/mod.rs b/core/src/sql/v1/statements/define/mod.rs similarity index 100% rename from lib/src/sql/statements/define/mod.rs rename to core/src/sql/v1/statements/define/mod.rs diff --git a/lib/src/sql/statements/define/model.rs b/core/src/sql/v1/statements/define/model.rs similarity index 100% rename from lib/src/sql/statements/define/model.rs rename to core/src/sql/v1/statements/define/model.rs diff --git a/lib/src/sql/statements/define/namespace.rs b/core/src/sql/v1/statements/define/namespace.rs similarity index 100% rename from lib/src/sql/statements/define/namespace.rs rename to core/src/sql/v1/statements/define/namespace.rs diff --git a/lib/src/sql/statements/define/param.rs b/core/src/sql/v1/statements/define/param.rs similarity index 100% rename from lib/src/sql/statements/define/param.rs rename to core/src/sql/v1/statements/define/param.rs diff --git a/lib/src/sql/statements/define/scope.rs b/core/src/sql/v1/statements/define/scope.rs similarity index 100% rename from lib/src/sql/statements/define/scope.rs rename to core/src/sql/v1/statements/define/scope.rs diff --git a/lib/src/sql/statements/define/table.rs b/core/src/sql/v1/statements/define/table.rs similarity index 100% rename from lib/src/sql/statements/define/table.rs rename to core/src/sql/v1/statements/define/table.rs diff --git a/lib/src/sql/statements/define/token.rs b/core/src/sql/v1/statements/define/token.rs similarity index 100% rename from lib/src/sql/statements/define/token.rs rename to core/src/sql/v1/statements/define/token.rs diff --git a/lib/src/sql/statements/define/user.rs b/core/src/sql/v1/statements/define/user.rs similarity index 100% rename from lib/src/sql/statements/define/user.rs rename to core/src/sql/v1/statements/define/user.rs diff --git a/lib/src/sql/statements/delete.rs b/core/src/sql/v1/statements/delete.rs similarity index 100% rename from lib/src/sql/statements/delete.rs rename to core/src/sql/v1/statements/delete.rs diff --git a/lib/src/sql/statements/foreach.rs b/core/src/sql/v1/statements/foreach.rs similarity index 100% rename from lib/src/sql/statements/foreach.rs rename to core/src/sql/v1/statements/foreach.rs diff --git a/lib/src/sql/statements/ifelse.rs b/core/src/sql/v1/statements/ifelse.rs similarity index 100% rename from lib/src/sql/statements/ifelse.rs rename to core/src/sql/v1/statements/ifelse.rs diff --git a/lib/src/sql/statements/info.rs b/core/src/sql/v1/statements/info.rs similarity index 100% rename from lib/src/sql/statements/info.rs rename to core/src/sql/v1/statements/info.rs diff --git a/lib/src/sql/statements/insert.rs b/core/src/sql/v1/statements/insert.rs similarity index 100% rename from lib/src/sql/statements/insert.rs rename to core/src/sql/v1/statements/insert.rs diff --git a/lib/src/sql/statements/kill.rs b/core/src/sql/v1/statements/kill.rs similarity index 100% rename from lib/src/sql/statements/kill.rs rename to core/src/sql/v1/statements/kill.rs diff --git a/lib/src/sql/statements/live.rs b/core/src/sql/v1/statements/live.rs similarity index 95% rename from lib/src/sql/statements/live.rs rename to core/src/sql/v1/statements/live.rs index 07d9e80a..2850f57b 100644 --- a/lib/src/sql/statements/live.rs +++ b/core/src/sql/v1/statements/live.rs @@ -40,6 +40,16 @@ pub struct LiveStatement { } impl LiveStatement { + #[doc(hidden)] + pub fn new(expr: Fields) -> Self { + LiveStatement { + id: Uuid::new_v4(), + node: Uuid::new_v4(), + expr, + ..Default::default() + } + } + /// Creates a live statement from parts that can be set during a query. pub(crate) fn from_source_parts( expr: Fields, diff --git a/lib/src/sql/statements/mod.rs b/core/src/sql/v1/statements/mod.rs similarity index 100% rename from lib/src/sql/statements/mod.rs rename to core/src/sql/v1/statements/mod.rs diff --git a/lib/src/sql/statements/option.rs b/core/src/sql/v1/statements/option.rs similarity index 100% rename from lib/src/sql/statements/option.rs rename to core/src/sql/v1/statements/option.rs diff --git a/lib/src/sql/statements/output.rs b/core/src/sql/v1/statements/output.rs similarity index 100% rename from lib/src/sql/statements/output.rs rename to core/src/sql/v1/statements/output.rs diff --git a/lib/src/sql/statements/relate.rs b/core/src/sql/v1/statements/relate.rs similarity index 100% rename from lib/src/sql/statements/relate.rs rename to core/src/sql/v1/statements/relate.rs diff --git a/lib/src/sql/statements/remove/analyzer.rs b/core/src/sql/v1/statements/remove/analyzer.rs similarity index 100% rename from lib/src/sql/statements/remove/analyzer.rs rename to core/src/sql/v1/statements/remove/analyzer.rs diff --git a/lib/src/sql/statements/remove/database.rs b/core/src/sql/v1/statements/remove/database.rs similarity index 100% rename from lib/src/sql/statements/remove/database.rs rename to core/src/sql/v1/statements/remove/database.rs diff --git a/lib/src/sql/statements/remove/event.rs b/core/src/sql/v1/statements/remove/event.rs similarity index 100% rename from lib/src/sql/statements/remove/event.rs rename to core/src/sql/v1/statements/remove/event.rs diff --git a/lib/src/sql/statements/remove/field.rs b/core/src/sql/v1/statements/remove/field.rs similarity index 100% rename from lib/src/sql/statements/remove/field.rs rename to core/src/sql/v1/statements/remove/field.rs diff --git a/lib/src/sql/statements/remove/function.rs b/core/src/sql/v1/statements/remove/function.rs similarity index 100% rename from lib/src/sql/statements/remove/function.rs rename to core/src/sql/v1/statements/remove/function.rs diff --git a/lib/src/sql/statements/remove/index.rs b/core/src/sql/v1/statements/remove/index.rs similarity index 100% rename from lib/src/sql/statements/remove/index.rs rename to core/src/sql/v1/statements/remove/index.rs diff --git a/lib/src/sql/statements/remove/mod.rs b/core/src/sql/v1/statements/remove/mod.rs similarity index 100% rename from lib/src/sql/statements/remove/mod.rs rename to core/src/sql/v1/statements/remove/mod.rs diff --git a/lib/src/sql/statements/remove/model.rs b/core/src/sql/v1/statements/remove/model.rs similarity index 100% rename from lib/src/sql/statements/remove/model.rs rename to core/src/sql/v1/statements/remove/model.rs diff --git a/lib/src/sql/statements/remove/namespace.rs b/core/src/sql/v1/statements/remove/namespace.rs similarity index 100% rename from lib/src/sql/statements/remove/namespace.rs rename to core/src/sql/v1/statements/remove/namespace.rs diff --git a/lib/src/sql/statements/remove/param.rs b/core/src/sql/v1/statements/remove/param.rs similarity index 100% rename from lib/src/sql/statements/remove/param.rs rename to core/src/sql/v1/statements/remove/param.rs diff --git a/lib/src/sql/statements/remove/scope.rs b/core/src/sql/v1/statements/remove/scope.rs similarity index 100% rename from lib/src/sql/statements/remove/scope.rs rename to core/src/sql/v1/statements/remove/scope.rs diff --git a/lib/src/sql/statements/remove/table.rs b/core/src/sql/v1/statements/remove/table.rs similarity index 100% rename from lib/src/sql/statements/remove/table.rs rename to core/src/sql/v1/statements/remove/table.rs diff --git a/lib/src/sql/statements/remove/token.rs b/core/src/sql/v1/statements/remove/token.rs similarity index 100% rename from lib/src/sql/statements/remove/token.rs rename to core/src/sql/v1/statements/remove/token.rs diff --git a/lib/src/sql/statements/remove/user.rs b/core/src/sql/v1/statements/remove/user.rs similarity index 100% rename from lib/src/sql/statements/remove/user.rs rename to core/src/sql/v1/statements/remove/user.rs diff --git a/lib/src/sql/statements/select.rs b/core/src/sql/v1/statements/select.rs similarity index 100% rename from lib/src/sql/statements/select.rs rename to core/src/sql/v1/statements/select.rs diff --git a/lib/src/sql/statements/set.rs b/core/src/sql/v1/statements/set.rs similarity index 100% rename from lib/src/sql/statements/set.rs rename to core/src/sql/v1/statements/set.rs diff --git a/lib/src/sql/statements/show.rs b/core/src/sql/v1/statements/show.rs similarity index 100% rename from lib/src/sql/statements/show.rs rename to core/src/sql/v1/statements/show.rs diff --git a/lib/src/sql/statements/sleep.rs b/core/src/sql/v1/statements/sleep.rs similarity index 100% rename from lib/src/sql/statements/sleep.rs rename to core/src/sql/v1/statements/sleep.rs diff --git a/lib/src/sql/statements/throw.rs b/core/src/sql/v1/statements/throw.rs similarity index 100% rename from lib/src/sql/statements/throw.rs rename to core/src/sql/v1/statements/throw.rs diff --git a/lib/src/sql/statements/update.rs b/core/src/sql/v1/statements/update.rs similarity index 100% rename from lib/src/sql/statements/update.rs rename to core/src/sql/v1/statements/update.rs diff --git a/lib/src/sql/statements/use.rs b/core/src/sql/v1/statements/use.rs similarity index 100% rename from lib/src/sql/statements/use.rs rename to core/src/sql/v1/statements/use.rs diff --git a/lib/src/sql/strand.rs b/core/src/sql/v1/strand.rs similarity index 100% rename from lib/src/sql/strand.rs rename to core/src/sql/v1/strand.rs diff --git a/lib/src/sql/subquery.rs b/core/src/sql/v1/subquery.rs similarity index 100% rename from lib/src/sql/subquery.rs rename to core/src/sql/v1/subquery.rs diff --git a/lib/src/sql/table.rs b/core/src/sql/v1/table.rs similarity index 100% rename from lib/src/sql/table.rs rename to core/src/sql/v1/table.rs diff --git a/lib/src/sql/thing.rs b/core/src/sql/v1/thing.rs similarity index 100% rename from lib/src/sql/thing.rs rename to core/src/sql/v1/thing.rs diff --git a/lib/src/sql/timeout.rs b/core/src/sql/v1/timeout.rs similarity index 100% rename from lib/src/sql/timeout.rs rename to core/src/sql/v1/timeout.rs diff --git a/lib/src/sql/tokenizer.rs b/core/src/sql/v1/tokenizer.rs similarity index 100% rename from lib/src/sql/tokenizer.rs rename to core/src/sql/v1/tokenizer.rs diff --git a/lib/src/sql/uuid.rs b/core/src/sql/v1/uuid.rs similarity index 100% rename from lib/src/sql/uuid.rs rename to core/src/sql/v1/uuid.rs diff --git a/lib/src/sql/value/all.rs b/core/src/sql/v1/value/all.rs similarity index 100% rename from lib/src/sql/value/all.rs rename to core/src/sql/v1/value/all.rs diff --git a/lib/src/sql/value/changed.rs b/core/src/sql/v1/value/changed.rs similarity index 100% rename from lib/src/sql/value/changed.rs rename to core/src/sql/v1/value/changed.rs diff --git a/lib/src/sql/value/clear.rs b/core/src/sql/v1/value/clear.rs similarity index 100% rename from lib/src/sql/value/clear.rs rename to core/src/sql/v1/value/clear.rs diff --git a/lib/src/sql/value/compare.rs b/core/src/sql/v1/value/compare.rs similarity index 100% rename from lib/src/sql/value/compare.rs rename to core/src/sql/v1/value/compare.rs diff --git a/lib/src/sql/value/cut.rs b/core/src/sql/v1/value/cut.rs similarity index 100% rename from lib/src/sql/value/cut.rs rename to core/src/sql/v1/value/cut.rs diff --git a/lib/src/sql/value/dec.rs b/core/src/sql/v1/value/dec.rs similarity index 100% rename from lib/src/sql/value/dec.rs rename to core/src/sql/v1/value/dec.rs diff --git a/lib/src/sql/value/decrement.rs b/core/src/sql/v1/value/decrement.rs similarity index 100% rename from lib/src/sql/value/decrement.rs rename to core/src/sql/v1/value/decrement.rs diff --git a/lib/src/sql/value/def.rs b/core/src/sql/v1/value/def.rs similarity index 100% rename from lib/src/sql/value/def.rs rename to core/src/sql/v1/value/def.rs diff --git a/lib/src/sql/value/del.rs b/core/src/sql/v1/value/del.rs similarity index 100% rename from lib/src/sql/value/del.rs rename to core/src/sql/v1/value/del.rs diff --git a/lib/src/sql/value/diff.rs b/core/src/sql/v1/value/diff.rs similarity index 100% rename from lib/src/sql/value/diff.rs rename to core/src/sql/v1/value/diff.rs diff --git a/lib/src/sql/value/each.rs b/core/src/sql/v1/value/each.rs similarity index 100% rename from lib/src/sql/value/each.rs rename to core/src/sql/v1/value/each.rs diff --git a/lib/src/sql/value/every.rs b/core/src/sql/v1/value/every.rs similarity index 100% rename from lib/src/sql/value/every.rs rename to core/src/sql/v1/value/every.rs diff --git a/lib/src/sql/value/extend.rs b/core/src/sql/v1/value/extend.rs similarity index 100% rename from lib/src/sql/value/extend.rs rename to core/src/sql/v1/value/extend.rs diff --git a/lib/src/sql/value/fetch.rs b/core/src/sql/v1/value/fetch.rs similarity index 100% rename from lib/src/sql/value/fetch.rs rename to core/src/sql/v1/value/fetch.rs diff --git a/lib/src/sql/value/first.rs b/core/src/sql/v1/value/first.rs similarity index 100% rename from lib/src/sql/value/first.rs rename to core/src/sql/v1/value/first.rs diff --git a/lib/src/sql/value/flatten.rs b/core/src/sql/v1/value/flatten.rs similarity index 100% rename from lib/src/sql/value/flatten.rs rename to core/src/sql/v1/value/flatten.rs diff --git a/lib/src/sql/value/generate.rs b/core/src/sql/v1/value/generate.rs similarity index 100% rename from lib/src/sql/value/generate.rs rename to core/src/sql/v1/value/generate.rs diff --git a/lib/src/sql/value/get.rs b/core/src/sql/v1/value/get.rs similarity index 100% rename from lib/src/sql/value/get.rs rename to core/src/sql/v1/value/get.rs diff --git a/lib/src/sql/value/inc.rs b/core/src/sql/v1/value/inc.rs similarity index 100% rename from lib/src/sql/value/inc.rs rename to core/src/sql/v1/value/inc.rs diff --git a/lib/src/sql/value/increment.rs b/core/src/sql/v1/value/increment.rs similarity index 100% rename from lib/src/sql/value/increment.rs rename to core/src/sql/v1/value/increment.rs diff --git a/lib/src/sql/value/last.rs b/core/src/sql/v1/value/last.rs similarity index 100% rename from lib/src/sql/value/last.rs rename to core/src/sql/v1/value/last.rs diff --git a/lib/src/sql/value/merge.rs b/core/src/sql/v1/value/merge.rs similarity index 100% rename from lib/src/sql/value/merge.rs rename to core/src/sql/v1/value/merge.rs diff --git a/lib/src/sql/value/mod.rs b/core/src/sql/v1/value/mod.rs similarity index 100% rename from lib/src/sql/value/mod.rs rename to core/src/sql/v1/value/mod.rs diff --git a/lib/src/sql/value/patch.rs b/core/src/sql/v1/value/patch.rs similarity index 100% rename from lib/src/sql/value/patch.rs rename to core/src/sql/v1/value/patch.rs diff --git a/lib/src/sql/value/pick.rs b/core/src/sql/v1/value/pick.rs similarity index 100% rename from lib/src/sql/value/pick.rs rename to core/src/sql/v1/value/pick.rs diff --git a/lib/src/sql/value/put.rs b/core/src/sql/v1/value/put.rs similarity index 100% rename from lib/src/sql/value/put.rs rename to core/src/sql/v1/value/put.rs diff --git a/lib/src/sql/value/replace.rs b/core/src/sql/v1/value/replace.rs similarity index 100% rename from lib/src/sql/value/replace.rs rename to core/src/sql/v1/value/replace.rs diff --git a/lib/src/sql/value/rid.rs b/core/src/sql/v1/value/rid.rs similarity index 100% rename from lib/src/sql/value/rid.rs rename to core/src/sql/v1/value/rid.rs diff --git a/core/src/sql/v1/value/serde/de/mod.rs b/core/src/sql/v1/value/serde/de/mod.rs new file mode 100644 index 00000000..17a675ee --- /dev/null +++ b/core/src/sql/v1/value/serde/de/mod.rs @@ -0,0 +1,698 @@ +use crate::sql::constant::ConstantValue; +use crate::sql::id::Gen; +use crate::sql::Value; +use serde::de::DeserializeOwned; +use serde::Serialize; +use serde_json::json; +use serde_json::Map; +use serde_json::Value as JsonValue; + +impl From for serde_json::Value { + fn from(value: Value) -> Self { + into_json(value, true) + } +} + +fn into_json(value: Value, simplify: bool) -> JsonValue { + use crate::sql; + use crate::sql::Number; + + #[derive(Serialize)] + struct Array(Vec); + + impl From<(sql::Array, bool)> for Array { + fn from((arr, simplify): (sql::Array, bool)) -> Self { + let mut vec = Vec::with_capacity(arr.0.len()); + for value in arr.0 { + vec.push(into_json(value, simplify)); + } + Self(vec) + } + } + + #[derive(Serialize)] + struct Object(Map); + + impl From<(sql::Object, bool)> for Object { + fn from((obj, simplify): (sql::Object, bool)) -> Self { + let mut map = Map::with_capacity(obj.0.len()); + for (key, value) in obj.0 { + map.insert(key.to_owned(), into_json(value, simplify)); + } + Self(map) + } + } + + #[derive(Serialize)] + enum CoordinatesType { + Point, + LineString, + Polygon, + MultiPoint, + MultiLineString, + MultiPolygon, + } + + #[derive(Serialize)] + struct Coordinates { + #[serde(rename = "type")] + typ: CoordinatesType, + coordinates: JsonValue, + } + + struct GeometryCollection; + + impl Serialize for GeometryCollection { + fn serialize(&self, s: S) -> Result + where + S: serde::Serializer, + { + s.serialize_str("GeometryCollection") + } + } + + #[derive(Serialize)] + struct Geometries { + #[serde(rename = "type")] + typ: GeometryCollection, + geometries: Vec, + } + + #[derive(Serialize)] + struct Geometry(JsonValue); + + impl From for Geometry { + fn from(geo: sql::Geometry) -> Self { + Self(match geo { + sql::Geometry::Point(v) => json!(Coordinates { + typ: CoordinatesType::Point, + coordinates: vec![json!(v.x()), json!(v.y())].into(), + }), + sql::Geometry::Line(v) => json!(Coordinates { + typ: CoordinatesType::LineString, + coordinates: v + .points() + .map(|p| vec![json!(p.x()), json!(p.y())].into()) + .collect::>() + .into(), + }), + sql::Geometry::Polygon(v) => json!(Coordinates { + typ: CoordinatesType::Polygon, + coordinates: vec![v + .exterior() + .points() + .map(|p| vec![json!(p.x()), json!(p.y())].into()) + .collect::>()] + .into_iter() + .chain( + v.interiors() + .iter() + .map(|i| { + i.points() + .map(|p| vec![json!(p.x()), json!(p.y())].into()) + .collect::>() + }) + .collect::>>(), + ) + .collect::>>() + .into(), + }), + sql::Geometry::MultiPoint(v) => json!(Coordinates { + typ: CoordinatesType::MultiPoint, + coordinates: v + .0 + .iter() + .map(|v| vec![json!(v.x()), json!(v.y())].into()) + .collect::>() + .into() + }), + sql::Geometry::MultiLine(v) => json!(Coordinates { + typ: CoordinatesType::MultiLineString, + coordinates: v + .0 + .iter() + .map(|v| { + v.points() + .map(|v| vec![json!(v.x()), json!(v.y())].into()) + .collect::>() + }) + .collect::>>() + .into() + }), + sql::Geometry::MultiPolygon(v) => json!(Coordinates { + typ: CoordinatesType::MultiPolygon, + coordinates: v + .0 + .iter() + .map(|v| { + vec![v + .exterior() + .points() + .map(|p| vec![json!(p.x()), json!(p.y())].into()) + .collect::>()] + .into_iter() + .chain( + v.interiors() + .iter() + .map(|i| { + i.points() + .map(|p| vec![json!(p.x()), json!(p.y())].into()) + .collect::>() + }) + .collect::>>(), + ) + .collect::>>() + }) + .collect::>>>() + .into(), + }), + sql::Geometry::Collection(v) => json!(Geometries { + typ: GeometryCollection, + geometries: v.into_iter().map(Geometry::from).map(|x| x.0).collect(), + }), + }) + } + } + + #[derive(Serialize)] + enum Id { + Number(i64), + String(String), + Array(Array), + Object(Object), + } + + impl From<(sql::Id, bool)> for Id { + fn from((id, simplify): (sql::Id, bool)) -> Self { + match id { + sql::Id::Number(n) => Id::Number(n), + sql::Id::String(s) => Id::String(s), + sql::Id::Array(arr) => Id::Array((arr, simplify).into()), + sql::Id::Object(obj) => Id::Object((obj, simplify).into()), + sql::Id::Generate(v) => match v { + Gen::Rand => Id::from((sql::Id::rand(), simplify)), + Gen::Ulid => Id::from((sql::Id::ulid(), simplify)), + Gen::Uuid => Id::from((sql::Id::uuid(), simplify)), + }, + } + } + } + + #[derive(Serialize)] + struct Thing { + tb: String, + id: Id, + } + + impl From<(sql::Thing, bool)> for Thing { + fn from((thing, simplify): (sql::Thing, bool)) -> Self { + Self { + tb: thing.tb, + id: (thing.id, simplify).into(), + } + } + } + + match value { + // These value types are simple values which + // can be used in query responses sent to + // the client. + Value::None | Value::Null => JsonValue::Null, + Value::Bool(boolean) => boolean.into(), + Value::Number(number) => match number { + Number::Int(int) => int.into(), + Number::Float(float) => float.into(), + Number::Decimal(decimal) => json!(decimal), + }, + Value::Strand(strand) => strand.0.into(), + Value::Duration(duration) => match simplify { + true => duration.to_raw().into(), + false => json!(duration.0), + }, + Value::Datetime(datetime) => json!(datetime.0), + Value::Uuid(uuid) => json!(uuid.0), + Value::Array(array) => JsonValue::Array(Array::from((array, simplify)).0), + Value::Object(object) => JsonValue::Object(Object::from((object, simplify)).0), + Value::Geometry(geo) => match simplify { + true => Geometry::from(geo).0, + false => match geo { + sql::Geometry::Point(geo) => json!(geo), + sql::Geometry::Line(geo) => json!(geo), + sql::Geometry::Polygon(geo) => json!(geo), + sql::Geometry::MultiPoint(geo) => json!(geo), + sql::Geometry::MultiLine(geo) => json!(geo), + sql::Geometry::MultiPolygon(geo) => json!(geo), + sql::Geometry::Collection(geo) => json!(geo), + }, + }, + Value::Bytes(bytes) => json!(bytes.0), + Value::Thing(thing) => match simplify { + true => thing.to_string().into(), + false => json!(thing), + }, + // These Value types are un-computed values + // and are not used in query responses sent + // to the client. + Value::Param(param) => json!(param), + Value::Idiom(idiom) => json!(idiom), + Value::Table(table) => json!(table), + Value::Mock(mock) => json!(mock), + Value::Regex(regex) => json!(regex), + Value::Block(block) => json!(block), + Value::Range(range) => json!(range), + Value::Edges(edges) => json!(edges), + Value::Future(future) => json!(future), + Value::Constant(constant) => match simplify { + true => match constant.value() { + ConstantValue::Datetime(datetime) => json!(datetime.0), + ConstantValue::Float(float) => float.into(), + }, + false => json!(constant), + }, + Value::Cast(cast) => json!(cast), + Value::Function(function) => json!(function), + Value::Model(model) => json!(model), + Value::Query(query) => json!(query), + Value::Subquery(subquery) => json!(subquery), + Value::Expression(expression) => json!(expression), + } +} + +#[derive(Debug, Clone)] +#[doc(hidden)] +#[non_exhaustive] +pub struct FromValueError { + pub value: Value, + pub error: String, +} + +/// Deserializes a value `T` from `SurrealDB` [`Value`] +#[doc(hidden)] +pub fn from_value(value: Value) -> Result +where + T: DeserializeOwned, +{ + let json = into_json(value.clone(), false); + serde_json::from_value(json).map_err(|error| FromValueError { + value, + error: error.to_string(), + }) +} + +#[cfg(test)] +mod tests { + mod into_json { + use crate::sql; + use crate::sql::value::serde::de::from_value; + use crate::sql::value::serde::de::into_json; + use crate::sql::Value; + use chrono::DateTime; + use chrono::Utc; + use geo::line_string; + use geo::point; + use geo::polygon; + use geo::LineString; + use geo::MultiLineString; + use geo::MultiPoint; + use geo::MultiPolygon; + use geo::Point; + use geo::Polygon; + use rust_decimal::Decimal; + use serde_json::json; + use std::collections::BTreeMap; + use std::time::Duration; + use uuid::Uuid; + + #[test] + fn none_or_null() { + for value in [Value::None, Value::Null] { + let simple_json = into_json(value.clone(), true); + assert_eq!(simple_json, json!(null)); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(null)); + + let response: Option = from_value(value).unwrap(); + assert_eq!(response, None); + } + } + + #[test] + fn bool() { + for boolean in [true, false] { + let value = Value::Bool(boolean); + + let simple_json = into_json(value.clone(), true); + assert_eq!(simple_json, json!(boolean)); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(boolean)); + + let response: bool = from_value(value).unwrap(); + assert_eq!(response, boolean); + } + } + + #[test] + fn number_int() { + for num in [i64::MIN, 0, i64::MAX] { + let value = Value::Number(sql::Number::Int(num)); + + let simple_json = into_json(value.clone(), true); + assert_eq!(simple_json, json!(num)); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(num)); + + let response: i64 = from_value(value).unwrap(); + assert_eq!(response, num); + } + } + + #[test] + fn number_float() { + for num in [f64::NEG_INFINITY, f64::MIN, 0.0, f64::MAX, f64::INFINITY, f64::NAN] { + let value = Value::Number(sql::Number::Float(num)); + + let simple_json = into_json(value.clone(), true); + assert_eq!(simple_json, json!(num)); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(num)); + + if num.is_finite() { + let response: f64 = from_value(value).unwrap(); + assert_eq!(response, num); + } else { + let response: Option = from_value(value).unwrap(); + assert_eq!(response, None); + } + } + } + + #[test] + fn number_decimal() { + for num in [i64::MIN, 0, i64::MAX] { + let num = Decimal::new(num, 0); + let value = Value::Number(sql::Number::Decimal(num)); + + let simple_json = into_json(value.clone(), true); + assert_eq!(simple_json, json!(num.to_string())); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(num)); + + let response: Decimal = from_value(value).unwrap(); + assert_eq!(response, num); + } + } + + #[test] + fn strand() { + for str in ["", "foo"] { + let value = Value::Strand(str.into()); + + let simple_json = into_json(value.clone(), true); + assert_eq!(simple_json, json!(str)); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(str)); + + let response: String = from_value(value).unwrap(); + assert_eq!(response, str); + } + } + + #[test] + fn duration() { + for duration in [Duration::ZERO, Duration::MAX] { + let value = Value::Duration(duration.into()); + + let simple_json = into_json(value.clone(), true); + assert_eq!(simple_json, json!(sql::Duration(duration).to_raw())); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(duration)); + + let response: Duration = from_value(value).unwrap(); + assert_eq!(response, duration); + } + } + + #[test] + fn datetime() { + for datetime in [DateTime::::MIN_UTC, DateTime::::MAX_UTC] { + let value = Value::Datetime(datetime.into()); + + let simple_json = into_json(value.clone(), true); + assert_eq!(simple_json, json!(datetime)); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(datetime)); + + let response: DateTime = from_value(value).unwrap(); + assert_eq!(response, datetime); + } + } + + #[test] + fn uuid() { + for uuid in [Uuid::nil(), Uuid::max()] { + let value = Value::Uuid(uuid.into()); + + let simple_json = into_json(value.clone(), true); + assert_eq!(simple_json, json!(uuid)); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(uuid)); + + let response: Uuid = from_value(value).unwrap(); + assert_eq!(response, uuid); + } + } + + #[test] + fn array() { + for vec in [vec![], vec![true, false]] { + let value = + Value::Array(sql::Array(vec.iter().copied().map(Value::from).collect())); + + let simple_json = into_json(value.clone(), true); + assert_eq!(simple_json, json!(vec)); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(vec)); + + let response: Vec = from_value(value).unwrap(); + assert_eq!(response, vec); + } + } + + #[test] + fn object() { + for map in [BTreeMap::new(), map!("done".to_owned() => true)] { + let value = Value::Object(sql::Object( + map.iter().map(|(key, value)| (key.clone(), Value::from(*value))).collect(), + )); + + let simple_json = into_json(value.clone(), true); + assert_eq!(simple_json, json!(map)); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(map)); + + let response: BTreeMap = from_value(value).unwrap(); + assert_eq!(response, map); + } + } + + #[test] + fn geometry_point() { + let point = point! { x: 10., y: 20. }; + let value = Value::Geometry(sql::Geometry::Point(point)); + + let simple_json = into_json(value.clone(), true); + assert_eq!(simple_json, json!({ "type": "Point", "coordinates": [10., 20.]})); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(point)); + + let response: Point = from_value(value).unwrap(); + assert_eq!(response, point); + } + + #[test] + fn geometry_line() { + let line_string = line_string![ + ( x: 0., y: 0. ), + ( x: 10., y: 0. ), + ]; + let value = Value::Geometry(sql::Geometry::Line(line_string.clone())); + + let simple_json = into_json(value.clone(), true); + assert_eq!( + simple_json, + json!({ "type": "LineString", "coordinates": [[0., 0.], [10., 0.]]}) + ); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(line_string)); + + let response: LineString = from_value(value).unwrap(); + assert_eq!(response, line_string); + } + + #[test] + fn geometry_polygon() { + let polygon = polygon![ + (x: -111., y: 45.), + (x: -111., y: 41.), + (x: -104., y: 41.), + (x: -104., y: 45.), + ]; + let value = Value::Geometry(sql::Geometry::Polygon(polygon.clone())); + + let simple_json = into_json(value.clone(), true); + assert_eq!( + simple_json, + json!({ "type": "Polygon", "coordinates": [[ + [-111., 45.], + [-111., 41.], + [-104., 41.], + [-104., 45.], + [-111., 45.], + ]]}) + ); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(polygon)); + + let response: Polygon = from_value(value).unwrap(); + assert_eq!(response, polygon); + } + + #[test] + fn geometry_multi_point() { + let multi_point: MultiPoint = + vec![point! { x: 0., y: 0. }, point! { x: 1., y: 2. }].into(); + let value = Value::Geometry(sql::Geometry::MultiPoint(multi_point.clone())); + + let simple_json = into_json(value.clone(), true); + assert_eq!( + simple_json, + json!({ "type": "MultiPoint", "coordinates": [[0., 0.], [1., 2.]]}) + ); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(multi_point)); + + let response: MultiPoint = from_value(value).unwrap(); + assert_eq!(response, multi_point); + } + + #[test] + fn geometry_multi_line() { + let multi_line = MultiLineString::new(vec![line_string![ + ( x: 0., y: 0. ), + ( x: 1., y: 2. ), + ]]); + let value = Value::Geometry(sql::Geometry::MultiLine(multi_line.clone())); + + let simple_json = into_json(value.clone(), true); + assert_eq!( + simple_json, + json!({ "type": "MultiLineString", "coordinates": [[[0., 0.], [1., 2.]]]}) + ); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(multi_line)); + + let response: MultiLineString = from_value(value).unwrap(); + assert_eq!(response, multi_line); + } + + #[test] + fn geometry_multi_polygon() { + let multi_polygon: MultiPolygon = vec![polygon![ + (x: -111., y: 45.), + (x: -111., y: 41.), + (x: -104., y: 41.), + (x: -104., y: 45.), + ]] + .into(); + let value = Value::Geometry(sql::Geometry::MultiPolygon(multi_polygon.clone())); + + let simple_json = into_json(value.clone(), true); + assert_eq!( + simple_json, + json!({ "type": "MultiPolygon", "coordinates": [[[ + [-111., 45.], + [-111., 41.], + [-104., 41.], + [-104., 45.], + [-111., 45.], + ]]]}) + ); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(multi_polygon)); + + let response: MultiPolygon = from_value(value).unwrap(); + assert_eq!(response, multi_polygon); + } + + #[test] + fn geometry_collection() { + for geometries in [vec![], vec![sql::Geometry::Point(point! { x: 10., y: 20. })]] { + let value = Value::Geometry(geometries.clone().into()); + + let simple_json = into_json(value.clone(), true); + assert_eq!( + simple_json, + json!({ + "type": "GeometryCollection", + "geometries": geometries.clone().into_iter().map(|geo| into_json(Value::from(geo), true)).collect::>(), + }) + ); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(geometries)); + + let response: Vec = from_value(value).unwrap(); + assert_eq!(response, geometries); + } + } + + #[test] + fn bytes() { + for bytes in [vec![], b"foo".to_vec()] { + let value = Value::Bytes(sql::Bytes(bytes.clone())); + + let simple_json = into_json(value.clone(), true); + assert_eq!(simple_json, json!(bytes)); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(bytes)); + + let response: Vec = from_value(value).unwrap(); + assert_eq!(response, bytes); + } + } + + #[test] + fn thing() { + let record_id = "foo:bar"; + let thing = sql::thing(record_id).unwrap(); + let value = Value::Thing(thing.clone()); + + let simple_json = into_json(value.clone(), true); + assert_eq!(simple_json, json!(record_id)); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(thing)); + + let response: sql::Thing = from_value(value).unwrap(); + assert_eq!(response, thing); + } + } +} diff --git a/core/src/sql/v1/value/serde/mod.rs b/core/src/sql/v1/value/serde/mod.rs new file mode 100644 index 00000000..227280c9 --- /dev/null +++ b/core/src/sql/v1/value/serde/mod.rs @@ -0,0 +1,5 @@ +mod de; +mod ser; + +pub use de::{from_value, FromValueError}; +pub use ser::to_value; diff --git a/lib/src/sql/value/serde/ser/algorithm/mod.rs b/core/src/sql/v1/value/serde/ser/algorithm/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/algorithm/mod.rs rename to core/src/sql/v1/value/serde/ser/algorithm/mod.rs diff --git a/lib/src/sql/value/serde/ser/base/mod.rs b/core/src/sql/v1/value/serde/ser/base/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/base/mod.rs rename to core/src/sql/v1/value/serde/ser/base/mod.rs diff --git a/lib/src/sql/value/serde/ser/base/opt.rs b/core/src/sql/v1/value/serde/ser/base/opt.rs similarity index 100% rename from lib/src/sql/value/serde/ser/base/opt.rs rename to core/src/sql/v1/value/serde/ser/base/opt.rs diff --git a/lib/src/sql/value/serde/ser/block/entry/mod.rs b/core/src/sql/v1/value/serde/ser/block/entry/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/block/entry/mod.rs rename to core/src/sql/v1/value/serde/ser/block/entry/mod.rs diff --git a/lib/src/sql/value/serde/ser/block/entry/vec.rs b/core/src/sql/v1/value/serde/ser/block/entry/vec.rs similarity index 100% rename from lib/src/sql/value/serde/ser/block/entry/vec.rs rename to core/src/sql/v1/value/serde/ser/block/entry/vec.rs diff --git a/lib/src/sql/value/serde/ser/block/mod.rs b/core/src/sql/v1/value/serde/ser/block/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/block/mod.rs rename to core/src/sql/v1/value/serde/ser/block/mod.rs diff --git a/lib/src/sql/value/serde/ser/cast/mod.rs b/core/src/sql/v1/value/serde/ser/cast/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/cast/mod.rs rename to core/src/sql/v1/value/serde/ser/cast/mod.rs diff --git a/lib/src/sql/value/serde/ser/changefeed/mod.rs b/core/src/sql/v1/value/serde/ser/changefeed/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/changefeed/mod.rs rename to core/src/sql/v1/value/serde/ser/changefeed/mod.rs diff --git a/lib/src/sql/value/serde/ser/changefeed/opt.rs b/core/src/sql/v1/value/serde/ser/changefeed/opt.rs similarity index 100% rename from lib/src/sql/value/serde/ser/changefeed/opt.rs rename to core/src/sql/v1/value/serde/ser/changefeed/opt.rs diff --git a/lib/src/sql/value/serde/ser/cond/mod.rs b/core/src/sql/v1/value/serde/ser/cond/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/cond/mod.rs rename to core/src/sql/v1/value/serde/ser/cond/mod.rs diff --git a/lib/src/sql/value/serde/ser/cond/opt.rs b/core/src/sql/v1/value/serde/ser/cond/opt.rs similarity index 100% rename from lib/src/sql/value/serde/ser/cond/opt.rs rename to core/src/sql/v1/value/serde/ser/cond/opt.rs diff --git a/lib/src/sql/value/serde/ser/constant/mod.rs b/core/src/sql/v1/value/serde/ser/constant/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/constant/mod.rs rename to core/src/sql/v1/value/serde/ser/constant/mod.rs diff --git a/lib/src/sql/value/serde/ser/data/mod.rs b/core/src/sql/v1/value/serde/ser/data/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/data/mod.rs rename to core/src/sql/v1/value/serde/ser/data/mod.rs diff --git a/lib/src/sql/value/serde/ser/data/opt.rs b/core/src/sql/v1/value/serde/ser/data/opt.rs similarity index 100% rename from lib/src/sql/value/serde/ser/data/opt.rs rename to core/src/sql/v1/value/serde/ser/data/opt.rs diff --git a/lib/src/sql/value/serde/ser/datetime/mod.rs b/core/src/sql/v1/value/serde/ser/datetime/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/datetime/mod.rs rename to core/src/sql/v1/value/serde/ser/datetime/mod.rs diff --git a/lib/src/sql/value/serde/ser/decimal/mod.rs b/core/src/sql/v1/value/serde/ser/decimal/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/decimal/mod.rs rename to core/src/sql/v1/value/serde/ser/decimal/mod.rs diff --git a/lib/src/sql/value/serde/ser/dir/mod.rs b/core/src/sql/v1/value/serde/ser/dir/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/dir/mod.rs rename to core/src/sql/v1/value/serde/ser/dir/mod.rs diff --git a/lib/src/sql/value/serde/ser/distance/mod.rs b/core/src/sql/v1/value/serde/ser/distance/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/distance/mod.rs rename to core/src/sql/v1/value/serde/ser/distance/mod.rs diff --git a/lib/src/sql/value/serde/ser/duration/mod.rs b/core/src/sql/v1/value/serde/ser/duration/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/duration/mod.rs rename to core/src/sql/v1/value/serde/ser/duration/mod.rs diff --git a/lib/src/sql/value/serde/ser/duration/opt.rs b/core/src/sql/v1/value/serde/ser/duration/opt.rs similarity index 100% rename from lib/src/sql/value/serde/ser/duration/opt.rs rename to core/src/sql/v1/value/serde/ser/duration/opt.rs diff --git a/lib/src/sql/value/serde/ser/edges/mod.rs b/core/src/sql/v1/value/serde/ser/edges/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/edges/mod.rs rename to core/src/sql/v1/value/serde/ser/edges/mod.rs diff --git a/lib/src/sql/value/serde/ser/explain/mod.rs b/core/src/sql/v1/value/serde/ser/explain/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/explain/mod.rs rename to core/src/sql/v1/value/serde/ser/explain/mod.rs diff --git a/lib/src/sql/value/serde/ser/explain/opt.rs b/core/src/sql/v1/value/serde/ser/explain/opt.rs similarity index 100% rename from lib/src/sql/value/serde/ser/explain/opt.rs rename to core/src/sql/v1/value/serde/ser/explain/opt.rs diff --git a/lib/src/sql/value/serde/ser/expression/mod.rs b/core/src/sql/v1/value/serde/ser/expression/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/expression/mod.rs rename to core/src/sql/v1/value/serde/ser/expression/mod.rs diff --git a/lib/src/sql/value/serde/ser/fetch/mod.rs b/core/src/sql/v1/value/serde/ser/fetch/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/fetch/mod.rs rename to core/src/sql/v1/value/serde/ser/fetch/mod.rs diff --git a/lib/src/sql/value/serde/ser/fetch/vec/mod.rs b/core/src/sql/v1/value/serde/ser/fetch/vec/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/fetch/vec/mod.rs rename to core/src/sql/v1/value/serde/ser/fetch/vec/mod.rs diff --git a/lib/src/sql/value/serde/ser/fetch/vec/opt.rs b/core/src/sql/v1/value/serde/ser/fetch/vec/opt.rs similarity index 100% rename from lib/src/sql/value/serde/ser/fetch/vec/opt.rs rename to core/src/sql/v1/value/serde/ser/fetch/vec/opt.rs diff --git a/lib/src/sql/value/serde/ser/fetchs/mod.rs b/core/src/sql/v1/value/serde/ser/fetchs/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/fetchs/mod.rs rename to core/src/sql/v1/value/serde/ser/fetchs/mod.rs diff --git a/lib/src/sql/value/serde/ser/fetchs/opt.rs b/core/src/sql/v1/value/serde/ser/fetchs/opt.rs similarity index 100% rename from lib/src/sql/value/serde/ser/fetchs/opt.rs rename to core/src/sql/v1/value/serde/ser/fetchs/opt.rs diff --git a/lib/src/sql/value/serde/ser/field/mod.rs b/core/src/sql/v1/value/serde/ser/field/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/field/mod.rs rename to core/src/sql/v1/value/serde/ser/field/mod.rs diff --git a/lib/src/sql/value/serde/ser/field/vec.rs b/core/src/sql/v1/value/serde/ser/field/vec.rs similarity index 100% rename from lib/src/sql/value/serde/ser/field/vec.rs rename to core/src/sql/v1/value/serde/ser/field/vec.rs diff --git a/lib/src/sql/value/serde/ser/fields/mod.rs b/core/src/sql/v1/value/serde/ser/fields/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/fields/mod.rs rename to core/src/sql/v1/value/serde/ser/fields/mod.rs diff --git a/lib/src/sql/value/serde/ser/filter/mod.rs b/core/src/sql/v1/value/serde/ser/filter/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/filter/mod.rs rename to core/src/sql/v1/value/serde/ser/filter/mod.rs diff --git a/lib/src/sql/value/serde/ser/filter/vec/mod.rs b/core/src/sql/v1/value/serde/ser/filter/vec/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/filter/vec/mod.rs rename to core/src/sql/v1/value/serde/ser/filter/vec/mod.rs diff --git a/lib/src/sql/value/serde/ser/filter/vec/opt.rs b/core/src/sql/v1/value/serde/ser/filter/vec/opt.rs similarity index 100% rename from lib/src/sql/value/serde/ser/filter/vec/opt.rs rename to core/src/sql/v1/value/serde/ser/filter/vec/opt.rs diff --git a/lib/src/sql/value/serde/ser/function/mod.rs b/core/src/sql/v1/value/serde/ser/function/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/function/mod.rs rename to core/src/sql/v1/value/serde/ser/function/mod.rs diff --git a/lib/src/sql/value/serde/ser/geometry/coord/mod.rs b/core/src/sql/v1/value/serde/ser/geometry/coord/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/geometry/coord/mod.rs rename to core/src/sql/v1/value/serde/ser/geometry/coord/mod.rs diff --git a/lib/src/sql/value/serde/ser/geometry/coord/vec.rs b/core/src/sql/v1/value/serde/ser/geometry/coord/vec.rs similarity index 100% rename from lib/src/sql/value/serde/ser/geometry/coord/vec.rs rename to core/src/sql/v1/value/serde/ser/geometry/coord/vec.rs diff --git a/lib/src/sql/value/serde/ser/geometry/line_string/mod.rs b/core/src/sql/v1/value/serde/ser/geometry/line_string/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/geometry/line_string/mod.rs rename to core/src/sql/v1/value/serde/ser/geometry/line_string/mod.rs diff --git a/lib/src/sql/value/serde/ser/geometry/line_string/vec.rs b/core/src/sql/v1/value/serde/ser/geometry/line_string/vec.rs similarity index 100% rename from lib/src/sql/value/serde/ser/geometry/line_string/vec.rs rename to core/src/sql/v1/value/serde/ser/geometry/line_string/vec.rs diff --git a/lib/src/sql/value/serde/ser/geometry/mod.rs b/core/src/sql/v1/value/serde/ser/geometry/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/geometry/mod.rs rename to core/src/sql/v1/value/serde/ser/geometry/mod.rs diff --git a/lib/src/sql/value/serde/ser/geometry/point/mod.rs b/core/src/sql/v1/value/serde/ser/geometry/point/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/geometry/point/mod.rs rename to core/src/sql/v1/value/serde/ser/geometry/point/mod.rs diff --git a/lib/src/sql/value/serde/ser/geometry/point/vec.rs b/core/src/sql/v1/value/serde/ser/geometry/point/vec.rs similarity index 100% rename from lib/src/sql/value/serde/ser/geometry/point/vec.rs rename to core/src/sql/v1/value/serde/ser/geometry/point/vec.rs diff --git a/lib/src/sql/value/serde/ser/geometry/polygon/mod.rs b/core/src/sql/v1/value/serde/ser/geometry/polygon/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/geometry/polygon/mod.rs rename to core/src/sql/v1/value/serde/ser/geometry/polygon/mod.rs diff --git a/lib/src/sql/value/serde/ser/geometry/polygon/vec.rs b/core/src/sql/v1/value/serde/ser/geometry/polygon/vec.rs similarity index 100% rename from lib/src/sql/value/serde/ser/geometry/polygon/vec.rs rename to core/src/sql/v1/value/serde/ser/geometry/polygon/vec.rs diff --git a/lib/src/sql/value/serde/ser/geometry/vec.rs b/core/src/sql/v1/value/serde/ser/geometry/vec.rs similarity index 100% rename from lib/src/sql/value/serde/ser/geometry/vec.rs rename to core/src/sql/v1/value/serde/ser/geometry/vec.rs diff --git a/lib/src/sql/value/serde/ser/graph/mod.rs b/core/src/sql/v1/value/serde/ser/graph/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/graph/mod.rs rename to core/src/sql/v1/value/serde/ser/graph/mod.rs diff --git a/lib/src/sql/value/serde/ser/group/mod.rs b/core/src/sql/v1/value/serde/ser/group/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/group/mod.rs rename to core/src/sql/v1/value/serde/ser/group/mod.rs diff --git a/lib/src/sql/value/serde/ser/group/vec/mod.rs b/core/src/sql/v1/value/serde/ser/group/vec/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/group/vec/mod.rs rename to core/src/sql/v1/value/serde/ser/group/vec/mod.rs diff --git a/lib/src/sql/value/serde/ser/group/vec/opt.rs b/core/src/sql/v1/value/serde/ser/group/vec/opt.rs similarity index 100% rename from lib/src/sql/value/serde/ser/group/vec/opt.rs rename to core/src/sql/v1/value/serde/ser/group/vec/opt.rs diff --git a/lib/src/sql/value/serde/ser/id/mod.rs b/core/src/sql/v1/value/serde/ser/id/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/id/mod.rs rename to core/src/sql/v1/value/serde/ser/id/mod.rs diff --git a/lib/src/sql/value/serde/ser/ident/mod.rs b/core/src/sql/v1/value/serde/ser/ident/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/ident/mod.rs rename to core/src/sql/v1/value/serde/ser/ident/mod.rs diff --git a/lib/src/sql/value/serde/ser/ident/vec.rs b/core/src/sql/v1/value/serde/ser/ident/vec.rs similarity index 100% rename from lib/src/sql/value/serde/ser/ident/vec.rs rename to core/src/sql/v1/value/serde/ser/ident/vec.rs diff --git a/lib/src/sql/value/serde/ser/idiom/mod.rs b/core/src/sql/v1/value/serde/ser/idiom/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/idiom/mod.rs rename to core/src/sql/v1/value/serde/ser/idiom/mod.rs diff --git a/lib/src/sql/value/serde/ser/idiom/vec/mod.rs b/core/src/sql/v1/value/serde/ser/idiom/vec/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/idiom/vec/mod.rs rename to core/src/sql/v1/value/serde/ser/idiom/vec/mod.rs diff --git a/lib/src/sql/value/serde/ser/idiom/vec/opt.rs b/core/src/sql/v1/value/serde/ser/idiom/vec/opt.rs similarity index 100% rename from lib/src/sql/value/serde/ser/idiom/vec/opt.rs rename to core/src/sql/v1/value/serde/ser/idiom/vec/opt.rs diff --git a/lib/src/sql/value/serde/ser/index/mod.rs b/core/src/sql/v1/value/serde/ser/index/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/index/mod.rs rename to core/src/sql/v1/value/serde/ser/index/mod.rs diff --git a/lib/src/sql/value/serde/ser/index/mtreeparams.rs b/core/src/sql/v1/value/serde/ser/index/mtreeparams.rs similarity index 100% rename from lib/src/sql/value/serde/ser/index/mtreeparams.rs rename to core/src/sql/v1/value/serde/ser/index/mtreeparams.rs diff --git a/lib/src/sql/value/serde/ser/index/searchparams.rs b/core/src/sql/v1/value/serde/ser/index/searchparams.rs similarity index 100% rename from lib/src/sql/value/serde/ser/index/searchparams.rs rename to core/src/sql/v1/value/serde/ser/index/searchparams.rs diff --git a/lib/src/sql/value/serde/ser/kind/mod.rs b/core/src/sql/v1/value/serde/ser/kind/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/kind/mod.rs rename to core/src/sql/v1/value/serde/ser/kind/mod.rs diff --git a/lib/src/sql/value/serde/ser/kind/opt.rs b/core/src/sql/v1/value/serde/ser/kind/opt.rs similarity index 100% rename from lib/src/sql/value/serde/ser/kind/opt.rs rename to core/src/sql/v1/value/serde/ser/kind/opt.rs diff --git a/lib/src/sql/value/serde/ser/kind/vec.rs b/core/src/sql/v1/value/serde/ser/kind/vec.rs similarity index 100% rename from lib/src/sql/value/serde/ser/kind/vec.rs rename to core/src/sql/v1/value/serde/ser/kind/vec.rs diff --git a/lib/src/sql/value/serde/ser/language/mod.rs b/core/src/sql/v1/value/serde/ser/language/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/language/mod.rs rename to core/src/sql/v1/value/serde/ser/language/mod.rs diff --git a/lib/src/sql/value/serde/ser/limit/mod.rs b/core/src/sql/v1/value/serde/ser/limit/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/limit/mod.rs rename to core/src/sql/v1/value/serde/ser/limit/mod.rs diff --git a/lib/src/sql/value/serde/ser/limit/opt.rs b/core/src/sql/v1/value/serde/ser/limit/opt.rs similarity index 100% rename from lib/src/sql/value/serde/ser/limit/opt.rs rename to core/src/sql/v1/value/serde/ser/limit/opt.rs diff --git a/lib/src/sql/value/serde/ser/mock/mod.rs b/core/src/sql/v1/value/serde/ser/mock/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/mock/mod.rs rename to core/src/sql/v1/value/serde/ser/mock/mod.rs diff --git a/lib/src/sql/value/serde/ser/mod.rs b/core/src/sql/v1/value/serde/ser/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/mod.rs rename to core/src/sql/v1/value/serde/ser/mod.rs diff --git a/lib/src/sql/value/serde/ser/number/mod.rs b/core/src/sql/v1/value/serde/ser/number/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/number/mod.rs rename to core/src/sql/v1/value/serde/ser/number/mod.rs diff --git a/lib/src/sql/value/serde/ser/operator/mod.rs b/core/src/sql/v1/value/serde/ser/operator/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/operator/mod.rs rename to core/src/sql/v1/value/serde/ser/operator/mod.rs diff --git a/lib/src/sql/value/serde/ser/order/mod.rs b/core/src/sql/v1/value/serde/ser/order/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/order/mod.rs rename to core/src/sql/v1/value/serde/ser/order/mod.rs diff --git a/lib/src/sql/value/serde/ser/order/vec/mod.rs b/core/src/sql/v1/value/serde/ser/order/vec/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/order/vec/mod.rs rename to core/src/sql/v1/value/serde/ser/order/vec/mod.rs diff --git a/lib/src/sql/value/serde/ser/order/vec/opt.rs b/core/src/sql/v1/value/serde/ser/order/vec/opt.rs similarity index 100% rename from lib/src/sql/value/serde/ser/order/vec/opt.rs rename to core/src/sql/v1/value/serde/ser/order/vec/opt.rs diff --git a/lib/src/sql/value/serde/ser/output/mod.rs b/core/src/sql/v1/value/serde/ser/output/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/output/mod.rs rename to core/src/sql/v1/value/serde/ser/output/mod.rs diff --git a/lib/src/sql/value/serde/ser/output/opt.rs b/core/src/sql/v1/value/serde/ser/output/opt.rs similarity index 100% rename from lib/src/sql/value/serde/ser/output/opt.rs rename to core/src/sql/v1/value/serde/ser/output/opt.rs diff --git a/lib/src/sql/value/serde/ser/part/mod.rs b/core/src/sql/v1/value/serde/ser/part/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/part/mod.rs rename to core/src/sql/v1/value/serde/ser/part/mod.rs diff --git a/lib/src/sql/value/serde/ser/part/vec/mod.rs b/core/src/sql/v1/value/serde/ser/part/vec/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/part/vec/mod.rs rename to core/src/sql/v1/value/serde/ser/part/vec/mod.rs diff --git a/lib/src/sql/value/serde/ser/part/vec/opt.rs b/core/src/sql/v1/value/serde/ser/part/vec/opt.rs similarity index 100% rename from lib/src/sql/value/serde/ser/part/vec/opt.rs rename to core/src/sql/v1/value/serde/ser/part/vec/opt.rs diff --git a/lib/src/sql/value/serde/ser/permission/mod.rs b/core/src/sql/v1/value/serde/ser/permission/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/permission/mod.rs rename to core/src/sql/v1/value/serde/ser/permission/mod.rs diff --git a/lib/src/sql/value/serde/ser/permissions/mod.rs b/core/src/sql/v1/value/serde/ser/permissions/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/permissions/mod.rs rename to core/src/sql/v1/value/serde/ser/permissions/mod.rs diff --git a/lib/src/sql/value/serde/ser/primitive/bool.rs b/core/src/sql/v1/value/serde/ser/primitive/bool.rs similarity index 100% rename from lib/src/sql/value/serde/ser/primitive/bool.rs rename to core/src/sql/v1/value/serde/ser/primitive/bool.rs diff --git a/lib/src/sql/value/serde/ser/primitive/f32.rs b/core/src/sql/v1/value/serde/ser/primitive/f32.rs similarity index 100% rename from lib/src/sql/value/serde/ser/primitive/f32.rs rename to core/src/sql/v1/value/serde/ser/primitive/f32.rs diff --git a/lib/src/sql/value/serde/ser/primitive/f64.rs b/core/src/sql/v1/value/serde/ser/primitive/f64.rs similarity index 100% rename from lib/src/sql/value/serde/ser/primitive/f64.rs rename to core/src/sql/v1/value/serde/ser/primitive/f64.rs diff --git a/lib/src/sql/value/serde/ser/primitive/i64.rs b/core/src/sql/v1/value/serde/ser/primitive/i64.rs similarity index 100% rename from lib/src/sql/value/serde/ser/primitive/i64.rs rename to core/src/sql/v1/value/serde/ser/primitive/i64.rs diff --git a/lib/src/sql/value/serde/ser/primitive/mod.rs b/core/src/sql/v1/value/serde/ser/primitive/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/primitive/mod.rs rename to core/src/sql/v1/value/serde/ser/primitive/mod.rs diff --git a/lib/src/sql/value/serde/ser/primitive/opt/mod.rs b/core/src/sql/v1/value/serde/ser/primitive/opt/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/primitive/opt/mod.rs rename to core/src/sql/v1/value/serde/ser/primitive/opt/mod.rs diff --git a/lib/src/sql/value/serde/ser/primitive/opt/u32.rs b/core/src/sql/v1/value/serde/ser/primitive/opt/u32.rs similarity index 100% rename from lib/src/sql/value/serde/ser/primitive/opt/u32.rs rename to core/src/sql/v1/value/serde/ser/primitive/opt/u32.rs diff --git a/lib/src/sql/value/serde/ser/primitive/opt/u64.rs b/core/src/sql/v1/value/serde/ser/primitive/opt/u64.rs similarity index 100% rename from lib/src/sql/value/serde/ser/primitive/opt/u64.rs rename to core/src/sql/v1/value/serde/ser/primitive/opt/u64.rs diff --git a/lib/src/sql/value/serde/ser/primitive/u16.rs b/core/src/sql/v1/value/serde/ser/primitive/u16.rs similarity index 100% rename from lib/src/sql/value/serde/ser/primitive/u16.rs rename to core/src/sql/v1/value/serde/ser/primitive/u16.rs diff --git a/lib/src/sql/value/serde/ser/primitive/u32.rs b/core/src/sql/v1/value/serde/ser/primitive/u32.rs similarity index 100% rename from lib/src/sql/value/serde/ser/primitive/u32.rs rename to core/src/sql/v1/value/serde/ser/primitive/u32.rs diff --git a/lib/src/sql/value/serde/ser/primitive/u64.rs b/core/src/sql/v1/value/serde/ser/primitive/u64.rs similarity index 100% rename from lib/src/sql/value/serde/ser/primitive/u64.rs rename to core/src/sql/v1/value/serde/ser/primitive/u64.rs diff --git a/lib/src/sql/value/serde/ser/primitive/u8.rs b/core/src/sql/v1/value/serde/ser/primitive/u8.rs similarity index 100% rename from lib/src/sql/value/serde/ser/primitive/u8.rs rename to core/src/sql/v1/value/serde/ser/primitive/u8.rs diff --git a/lib/src/sql/value/serde/ser/range/bound.rs b/core/src/sql/v1/value/serde/ser/range/bound.rs similarity index 100% rename from lib/src/sql/value/serde/ser/range/bound.rs rename to core/src/sql/v1/value/serde/ser/range/bound.rs diff --git a/lib/src/sql/value/serde/ser/range/mod.rs b/core/src/sql/v1/value/serde/ser/range/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/range/mod.rs rename to core/src/sql/v1/value/serde/ser/range/mod.rs diff --git a/lib/src/sql/value/serde/ser/scoring/mod.rs b/core/src/sql/v1/value/serde/ser/scoring/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/scoring/mod.rs rename to core/src/sql/v1/value/serde/ser/scoring/mod.rs diff --git a/lib/src/sql/value/serde/ser/split/mod.rs b/core/src/sql/v1/value/serde/ser/split/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/split/mod.rs rename to core/src/sql/v1/value/serde/ser/split/mod.rs diff --git a/lib/src/sql/value/serde/ser/split/vec/mod.rs b/core/src/sql/v1/value/serde/ser/split/vec/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/split/vec/mod.rs rename to core/src/sql/v1/value/serde/ser/split/vec/mod.rs diff --git a/lib/src/sql/value/serde/ser/split/vec/opt.rs b/core/src/sql/v1/value/serde/ser/split/vec/opt.rs similarity index 100% rename from lib/src/sql/value/serde/ser/split/vec/opt.rs rename to core/src/sql/v1/value/serde/ser/split/vec/opt.rs diff --git a/lib/src/sql/value/serde/ser/start/mod.rs b/core/src/sql/v1/value/serde/ser/start/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/start/mod.rs rename to core/src/sql/v1/value/serde/ser/start/mod.rs diff --git a/lib/src/sql/value/serde/ser/start/opt.rs b/core/src/sql/v1/value/serde/ser/start/opt.rs similarity index 100% rename from lib/src/sql/value/serde/ser/start/opt.rs rename to core/src/sql/v1/value/serde/ser/start/opt.rs diff --git a/lib/src/sql/value/serde/ser/statement/analyze.rs b/core/src/sql/v1/value/serde/ser/statement/analyze.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/analyze.rs rename to core/src/sql/v1/value/serde/ser/statement/analyze.rs diff --git a/lib/src/sql/value/serde/ser/statement/begin.rs b/core/src/sql/v1/value/serde/ser/statement/begin.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/begin.rs rename to core/src/sql/v1/value/serde/ser/statement/begin.rs diff --git a/lib/src/sql/value/serde/ser/statement/break.rs b/core/src/sql/v1/value/serde/ser/statement/break.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/break.rs rename to core/src/sql/v1/value/serde/ser/statement/break.rs diff --git a/lib/src/sql/value/serde/ser/statement/cancel.rs b/core/src/sql/v1/value/serde/ser/statement/cancel.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/cancel.rs rename to core/src/sql/v1/value/serde/ser/statement/cancel.rs diff --git a/lib/src/sql/value/serde/ser/statement/commit.rs b/core/src/sql/v1/value/serde/ser/statement/commit.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/commit.rs rename to core/src/sql/v1/value/serde/ser/statement/commit.rs diff --git a/lib/src/sql/value/serde/ser/statement/continue.rs b/core/src/sql/v1/value/serde/ser/statement/continue.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/continue.rs rename to core/src/sql/v1/value/serde/ser/statement/continue.rs diff --git a/lib/src/sql/value/serde/ser/statement/create.rs b/core/src/sql/v1/value/serde/ser/statement/create.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/create.rs rename to core/src/sql/v1/value/serde/ser/statement/create.rs diff --git a/lib/src/sql/value/serde/ser/statement/define/analyzer.rs b/core/src/sql/v1/value/serde/ser/statement/define/analyzer.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/define/analyzer.rs rename to core/src/sql/v1/value/serde/ser/statement/define/analyzer.rs diff --git a/lib/src/sql/value/serde/ser/statement/define/database.rs b/core/src/sql/v1/value/serde/ser/statement/define/database.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/define/database.rs rename to core/src/sql/v1/value/serde/ser/statement/define/database.rs diff --git a/lib/src/sql/value/serde/ser/statement/define/event.rs b/core/src/sql/v1/value/serde/ser/statement/define/event.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/define/event.rs rename to core/src/sql/v1/value/serde/ser/statement/define/event.rs diff --git a/lib/src/sql/value/serde/ser/statement/define/field.rs b/core/src/sql/v1/value/serde/ser/statement/define/field.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/define/field.rs rename to core/src/sql/v1/value/serde/ser/statement/define/field.rs diff --git a/lib/src/sql/value/serde/ser/statement/define/function.rs b/core/src/sql/v1/value/serde/ser/statement/define/function.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/define/function.rs rename to core/src/sql/v1/value/serde/ser/statement/define/function.rs diff --git a/lib/src/sql/value/serde/ser/statement/define/index.rs b/core/src/sql/v1/value/serde/ser/statement/define/index.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/define/index.rs rename to core/src/sql/v1/value/serde/ser/statement/define/index.rs diff --git a/lib/src/sql/value/serde/ser/statement/define/mod.rs b/core/src/sql/v1/value/serde/ser/statement/define/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/define/mod.rs rename to core/src/sql/v1/value/serde/ser/statement/define/mod.rs diff --git a/lib/src/sql/value/serde/ser/statement/define/namespace.rs b/core/src/sql/v1/value/serde/ser/statement/define/namespace.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/define/namespace.rs rename to core/src/sql/v1/value/serde/ser/statement/define/namespace.rs diff --git a/lib/src/sql/value/serde/ser/statement/define/param.rs b/core/src/sql/v1/value/serde/ser/statement/define/param.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/define/param.rs rename to core/src/sql/v1/value/serde/ser/statement/define/param.rs diff --git a/lib/src/sql/value/serde/ser/statement/define/scope.rs b/core/src/sql/v1/value/serde/ser/statement/define/scope.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/define/scope.rs rename to core/src/sql/v1/value/serde/ser/statement/define/scope.rs diff --git a/lib/src/sql/value/serde/ser/statement/define/table.rs b/core/src/sql/v1/value/serde/ser/statement/define/table.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/define/table.rs rename to core/src/sql/v1/value/serde/ser/statement/define/table.rs diff --git a/lib/src/sql/value/serde/ser/statement/define/token.rs b/core/src/sql/v1/value/serde/ser/statement/define/token.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/define/token.rs rename to core/src/sql/v1/value/serde/ser/statement/define/token.rs diff --git a/lib/src/sql/value/serde/ser/statement/define/user.rs b/core/src/sql/v1/value/serde/ser/statement/define/user.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/define/user.rs rename to core/src/sql/v1/value/serde/ser/statement/define/user.rs diff --git a/lib/src/sql/value/serde/ser/statement/delete.rs b/core/src/sql/v1/value/serde/ser/statement/delete.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/delete.rs rename to core/src/sql/v1/value/serde/ser/statement/delete.rs diff --git a/lib/src/sql/value/serde/ser/statement/ifelse.rs b/core/src/sql/v1/value/serde/ser/statement/ifelse.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/ifelse.rs rename to core/src/sql/v1/value/serde/ser/statement/ifelse.rs diff --git a/lib/src/sql/value/serde/ser/statement/info.rs b/core/src/sql/v1/value/serde/ser/statement/info.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/info.rs rename to core/src/sql/v1/value/serde/ser/statement/info.rs diff --git a/lib/src/sql/value/serde/ser/statement/insert.rs b/core/src/sql/v1/value/serde/ser/statement/insert.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/insert.rs rename to core/src/sql/v1/value/serde/ser/statement/insert.rs diff --git a/lib/src/sql/value/serde/ser/statement/kill.rs b/core/src/sql/v1/value/serde/ser/statement/kill.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/kill.rs rename to core/src/sql/v1/value/serde/ser/statement/kill.rs diff --git a/lib/src/sql/value/serde/ser/statement/live.rs b/core/src/sql/v1/value/serde/ser/statement/live.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/live.rs rename to core/src/sql/v1/value/serde/ser/statement/live.rs diff --git a/lib/src/sql/value/serde/ser/statement/mod.rs b/core/src/sql/v1/value/serde/ser/statement/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/mod.rs rename to core/src/sql/v1/value/serde/ser/statement/mod.rs diff --git a/lib/src/sql/value/serde/ser/statement/option.rs b/core/src/sql/v1/value/serde/ser/statement/option.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/option.rs rename to core/src/sql/v1/value/serde/ser/statement/option.rs diff --git a/lib/src/sql/value/serde/ser/statement/output.rs b/core/src/sql/v1/value/serde/ser/statement/output.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/output.rs rename to core/src/sql/v1/value/serde/ser/statement/output.rs diff --git a/lib/src/sql/value/serde/ser/statement/relate.rs b/core/src/sql/v1/value/serde/ser/statement/relate.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/relate.rs rename to core/src/sql/v1/value/serde/ser/statement/relate.rs diff --git a/lib/src/sql/value/serde/ser/statement/remove/analyzer.rs b/core/src/sql/v1/value/serde/ser/statement/remove/analyzer.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/remove/analyzer.rs rename to core/src/sql/v1/value/serde/ser/statement/remove/analyzer.rs diff --git a/lib/src/sql/value/serde/ser/statement/remove/database.rs b/core/src/sql/v1/value/serde/ser/statement/remove/database.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/remove/database.rs rename to core/src/sql/v1/value/serde/ser/statement/remove/database.rs diff --git a/lib/src/sql/value/serde/ser/statement/remove/event.rs b/core/src/sql/v1/value/serde/ser/statement/remove/event.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/remove/event.rs rename to core/src/sql/v1/value/serde/ser/statement/remove/event.rs diff --git a/lib/src/sql/value/serde/ser/statement/remove/field.rs b/core/src/sql/v1/value/serde/ser/statement/remove/field.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/remove/field.rs rename to core/src/sql/v1/value/serde/ser/statement/remove/field.rs diff --git a/lib/src/sql/value/serde/ser/statement/remove/function.rs b/core/src/sql/v1/value/serde/ser/statement/remove/function.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/remove/function.rs rename to core/src/sql/v1/value/serde/ser/statement/remove/function.rs diff --git a/lib/src/sql/value/serde/ser/statement/remove/index.rs b/core/src/sql/v1/value/serde/ser/statement/remove/index.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/remove/index.rs rename to core/src/sql/v1/value/serde/ser/statement/remove/index.rs diff --git a/lib/src/sql/value/serde/ser/statement/remove/mod.rs b/core/src/sql/v1/value/serde/ser/statement/remove/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/remove/mod.rs rename to core/src/sql/v1/value/serde/ser/statement/remove/mod.rs diff --git a/lib/src/sql/value/serde/ser/statement/remove/namespace.rs b/core/src/sql/v1/value/serde/ser/statement/remove/namespace.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/remove/namespace.rs rename to core/src/sql/v1/value/serde/ser/statement/remove/namespace.rs diff --git a/lib/src/sql/value/serde/ser/statement/remove/param.rs b/core/src/sql/v1/value/serde/ser/statement/remove/param.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/remove/param.rs rename to core/src/sql/v1/value/serde/ser/statement/remove/param.rs diff --git a/lib/src/sql/value/serde/ser/statement/remove/scope.rs b/core/src/sql/v1/value/serde/ser/statement/remove/scope.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/remove/scope.rs rename to core/src/sql/v1/value/serde/ser/statement/remove/scope.rs diff --git a/lib/src/sql/value/serde/ser/statement/remove/table.rs b/core/src/sql/v1/value/serde/ser/statement/remove/table.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/remove/table.rs rename to core/src/sql/v1/value/serde/ser/statement/remove/table.rs diff --git a/lib/src/sql/value/serde/ser/statement/remove/token.rs b/core/src/sql/v1/value/serde/ser/statement/remove/token.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/remove/token.rs rename to core/src/sql/v1/value/serde/ser/statement/remove/token.rs diff --git a/lib/src/sql/value/serde/ser/statement/remove/user.rs b/core/src/sql/v1/value/serde/ser/statement/remove/user.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/remove/user.rs rename to core/src/sql/v1/value/serde/ser/statement/remove/user.rs diff --git a/lib/src/sql/value/serde/ser/statement/select.rs b/core/src/sql/v1/value/serde/ser/statement/select.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/select.rs rename to core/src/sql/v1/value/serde/ser/statement/select.rs diff --git a/lib/src/sql/value/serde/ser/statement/set.rs b/core/src/sql/v1/value/serde/ser/statement/set.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/set.rs rename to core/src/sql/v1/value/serde/ser/statement/set.rs diff --git a/lib/src/sql/value/serde/ser/statement/show/mod.rs b/core/src/sql/v1/value/serde/ser/statement/show/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/show/mod.rs rename to core/src/sql/v1/value/serde/ser/statement/show/mod.rs diff --git a/lib/src/sql/value/serde/ser/statement/show/since.rs b/core/src/sql/v1/value/serde/ser/statement/show/since.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/show/since.rs rename to core/src/sql/v1/value/serde/ser/statement/show/since.rs diff --git a/lib/src/sql/value/serde/ser/statement/sleep.rs b/core/src/sql/v1/value/serde/ser/statement/sleep.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/sleep.rs rename to core/src/sql/v1/value/serde/ser/statement/sleep.rs diff --git a/lib/src/sql/value/serde/ser/statement/throw.rs b/core/src/sql/v1/value/serde/ser/statement/throw.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/throw.rs rename to core/src/sql/v1/value/serde/ser/statement/throw.rs diff --git a/lib/src/sql/value/serde/ser/statement/update.rs b/core/src/sql/v1/value/serde/ser/statement/update.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/update.rs rename to core/src/sql/v1/value/serde/ser/statement/update.rs diff --git a/lib/src/sql/value/serde/ser/statement/vec.rs b/core/src/sql/v1/value/serde/ser/statement/vec.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/vec.rs rename to core/src/sql/v1/value/serde/ser/statement/vec.rs diff --git a/lib/src/sql/value/serde/ser/statement/yuse.rs b/core/src/sql/v1/value/serde/ser/statement/yuse.rs similarity index 100% rename from lib/src/sql/value/serde/ser/statement/yuse.rs rename to core/src/sql/v1/value/serde/ser/statement/yuse.rs diff --git a/lib/src/sql/value/serde/ser/strand/mod.rs b/core/src/sql/v1/value/serde/ser/strand/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/strand/mod.rs rename to core/src/sql/v1/value/serde/ser/strand/mod.rs diff --git a/lib/src/sql/value/serde/ser/strand/opt.rs b/core/src/sql/v1/value/serde/ser/strand/opt.rs similarity index 100% rename from lib/src/sql/value/serde/ser/strand/opt.rs rename to core/src/sql/v1/value/serde/ser/strand/opt.rs diff --git a/lib/src/sql/value/serde/ser/string/mod.rs b/core/src/sql/v1/value/serde/ser/string/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/string/mod.rs rename to core/src/sql/v1/value/serde/ser/string/mod.rs diff --git a/lib/src/sql/value/serde/ser/string/opt.rs b/core/src/sql/v1/value/serde/ser/string/opt.rs similarity index 100% rename from lib/src/sql/value/serde/ser/string/opt.rs rename to core/src/sql/v1/value/serde/ser/string/opt.rs diff --git a/lib/src/sql/value/serde/ser/string/vec.rs b/core/src/sql/v1/value/serde/ser/string/vec.rs similarity index 100% rename from lib/src/sql/value/serde/ser/string/vec.rs rename to core/src/sql/v1/value/serde/ser/string/vec.rs diff --git a/lib/src/sql/value/serde/ser/subquery/mod.rs b/core/src/sql/v1/value/serde/ser/subquery/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/subquery/mod.rs rename to core/src/sql/v1/value/serde/ser/subquery/mod.rs diff --git a/lib/src/sql/value/serde/ser/table/mod.rs b/core/src/sql/v1/value/serde/ser/table/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/table/mod.rs rename to core/src/sql/v1/value/serde/ser/table/mod.rs diff --git a/lib/src/sql/value/serde/ser/table/opt.rs b/core/src/sql/v1/value/serde/ser/table/opt.rs similarity index 100% rename from lib/src/sql/value/serde/ser/table/opt.rs rename to core/src/sql/v1/value/serde/ser/table/opt.rs diff --git a/lib/src/sql/value/serde/ser/table/vec.rs b/core/src/sql/v1/value/serde/ser/table/vec.rs similarity index 100% rename from lib/src/sql/value/serde/ser/table/vec.rs rename to core/src/sql/v1/value/serde/ser/table/vec.rs diff --git a/lib/src/sql/value/serde/ser/thing/mod.rs b/core/src/sql/v1/value/serde/ser/thing/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/thing/mod.rs rename to core/src/sql/v1/value/serde/ser/thing/mod.rs diff --git a/lib/src/sql/value/serde/ser/timeout/mod.rs b/core/src/sql/v1/value/serde/ser/timeout/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/timeout/mod.rs rename to core/src/sql/v1/value/serde/ser/timeout/mod.rs diff --git a/lib/src/sql/value/serde/ser/timeout/opt.rs b/core/src/sql/v1/value/serde/ser/timeout/opt.rs similarity index 100% rename from lib/src/sql/value/serde/ser/timeout/opt.rs rename to core/src/sql/v1/value/serde/ser/timeout/opt.rs diff --git a/lib/src/sql/value/serde/ser/tokenizer/mod.rs b/core/src/sql/v1/value/serde/ser/tokenizer/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/tokenizer/mod.rs rename to core/src/sql/v1/value/serde/ser/tokenizer/mod.rs diff --git a/lib/src/sql/value/serde/ser/tokenizer/vec/mod.rs b/core/src/sql/v1/value/serde/ser/tokenizer/vec/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/tokenizer/vec/mod.rs rename to core/src/sql/v1/value/serde/ser/tokenizer/vec/mod.rs diff --git a/lib/src/sql/value/serde/ser/tokenizer/vec/opt.rs b/core/src/sql/v1/value/serde/ser/tokenizer/vec/opt.rs similarity index 100% rename from lib/src/sql/value/serde/ser/tokenizer/vec/opt.rs rename to core/src/sql/v1/value/serde/ser/tokenizer/vec/opt.rs diff --git a/lib/src/sql/value/serde/ser/uuid/mod.rs b/core/src/sql/v1/value/serde/ser/uuid/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/uuid/mod.rs rename to core/src/sql/v1/value/serde/ser/uuid/mod.rs diff --git a/lib/src/sql/value/serde/ser/uuid/opt.rs b/core/src/sql/v1/value/serde/ser/uuid/opt.rs similarity index 100% rename from lib/src/sql/value/serde/ser/uuid/opt.rs rename to core/src/sql/v1/value/serde/ser/uuid/opt.rs diff --git a/lib/src/sql/value/serde/ser/value/map.rs b/core/src/sql/v1/value/serde/ser/value/map.rs similarity index 100% rename from lib/src/sql/value/serde/ser/value/map.rs rename to core/src/sql/v1/value/serde/ser/value/map.rs diff --git a/lib/src/sql/value/serde/ser/value/mod.rs b/core/src/sql/v1/value/serde/ser/value/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/value/mod.rs rename to core/src/sql/v1/value/serde/ser/value/mod.rs diff --git a/lib/src/sql/value/serde/ser/value/opt.rs b/core/src/sql/v1/value/serde/ser/value/opt.rs similarity index 100% rename from lib/src/sql/value/serde/ser/value/opt.rs rename to core/src/sql/v1/value/serde/ser/value/opt.rs diff --git a/lib/src/sql/value/serde/ser/value/vec.rs b/core/src/sql/v1/value/serde/ser/value/vec.rs similarity index 100% rename from lib/src/sql/value/serde/ser/value/vec.rs rename to core/src/sql/v1/value/serde/ser/value/vec.rs diff --git a/lib/src/sql/value/serde/ser/vectortype/mod.rs b/core/src/sql/v1/value/serde/ser/vectortype/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/vectortype/mod.rs rename to core/src/sql/v1/value/serde/ser/vectortype/mod.rs diff --git a/lib/src/sql/value/serde/ser/version/mod.rs b/core/src/sql/v1/value/serde/ser/version/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/version/mod.rs rename to core/src/sql/v1/value/serde/ser/version/mod.rs diff --git a/lib/src/sql/value/serde/ser/version/opt.rs b/core/src/sql/v1/value/serde/ser/version/opt.rs similarity index 100% rename from lib/src/sql/value/serde/ser/version/opt.rs rename to core/src/sql/v1/value/serde/ser/version/opt.rs diff --git a/lib/src/sql/value/serde/ser/view/mod.rs b/core/src/sql/v1/value/serde/ser/view/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/view/mod.rs rename to core/src/sql/v1/value/serde/ser/view/mod.rs diff --git a/lib/src/sql/value/serde/ser/view/opt.rs b/core/src/sql/v1/value/serde/ser/view/opt.rs similarity index 100% rename from lib/src/sql/value/serde/ser/view/opt.rs rename to core/src/sql/v1/value/serde/ser/view/opt.rs diff --git a/lib/src/sql/value/serde/ser/with/mod.rs b/core/src/sql/v1/value/serde/ser/with/mod.rs similarity index 100% rename from lib/src/sql/value/serde/ser/with/mod.rs rename to core/src/sql/v1/value/serde/ser/with/mod.rs diff --git a/lib/src/sql/value/serde/ser/with/opt.rs b/core/src/sql/v1/value/serde/ser/with/opt.rs similarity index 100% rename from lib/src/sql/value/serde/ser/with/opt.rs rename to core/src/sql/v1/value/serde/ser/with/opt.rs diff --git a/lib/src/sql/value/set.rs b/core/src/sql/v1/value/set.rs similarity index 100% rename from lib/src/sql/value/set.rs rename to core/src/sql/v1/value/set.rs diff --git a/lib/src/sql/value/value.rs b/core/src/sql/v1/value/value.rs similarity index 99% rename from lib/src/sql/value/value.rs rename to core/src/sql/v1/value/value.rs index 340f41b5..8c1d68e8 100644 --- a/lib/src/sql/value/value.rs +++ b/core/src/sql/v1/value/value.rs @@ -1240,7 +1240,8 @@ impl Value { } /// Try to coerce this value to an `i64` - pub(crate) fn coerce_to_i64(self) -> Result { + #[doc(hidden)] + pub fn coerce_to_i64(self) -> Result { match self { // Allow any int number Value::Number(Number::Int(v)) => Ok(v), @@ -1969,7 +1970,8 @@ impl Value { } /// Try to convert this value to a `String` - pub(crate) fn convert_to_string(self) -> Result { + #[doc(hidden)] + pub fn convert_to_string(self) -> Result { match self { // Bytes can't convert to strings Value::Bytes(_) => Err(Error::ConvertTo { diff --git a/lib/src/sql/value/walk.rs b/core/src/sql/v1/value/walk.rs similarity index 100% rename from lib/src/sql/value/walk.rs rename to core/src/sql/v1/value/walk.rs diff --git a/lib/src/sql/version.rs b/core/src/sql/v1/version.rs similarity index 100% rename from lib/src/sql/version.rs rename to core/src/sql/v1/version.rs diff --git a/lib/src/sql/view.rs b/core/src/sql/v1/view.rs similarity index 100% rename from lib/src/sql/view.rs rename to core/src/sql/v1/view.rs diff --git a/lib/src/sql/with.rs b/core/src/sql/v1/with.rs similarity index 100% rename from lib/src/sql/with.rs rename to core/src/sql/v1/with.rs diff --git a/core/src/sql/v2/algorithm.rs b/core/src/sql/v2/algorithm.rs new file mode 100644 index 00000000..d62817ee --- /dev/null +++ b/core/src/sql/v2/algorithm.rs @@ -0,0 +1,50 @@ +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub enum Algorithm { + EdDSA, + Es256, + Es384, + Es512, + Hs256, + Hs384, + Hs512, + Ps256, + Ps384, + Ps512, + Rs256, + Rs384, + Rs512, + Jwks, // Not an argorithm. +} + +impl Default for Algorithm { + fn default() -> Self { + Self::Hs512 + } +} + +impl fmt::Display for Algorithm { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(match self { + Self::EdDSA => "EDDSA", + Self::Es256 => "ES256", + Self::Es384 => "ES384", + Self::Es512 => "ES512", + Self::Hs256 => "HS256", + Self::Hs384 => "HS384", + Self::Hs512 => "HS512", + Self::Ps256 => "PS256", + Self::Ps384 => "PS384", + Self::Ps512 => "PS512", + Self::Rs256 => "RS256", + Self::Rs384 => "RS384", + Self::Rs512 => "RS512", + Self::Jwks => "JWKS", // Not an algorithm. + }) + } +} diff --git a/core/src/sql/v2/arbitrary.rs b/core/src/sql/v2/arbitrary.rs new file mode 100644 index 00000000..bdad2bbc --- /dev/null +++ b/core/src/sql/v2/arbitrary.rs @@ -0,0 +1,46 @@ +use crate::sql::{ + changefeed::ChangeFeed, datetime::Datetime, duration::Duration, regex::Regex, + statements::SleepStatement, +}; +use arbitrary::{Arbitrary, Result, Unstructured}; +use regex_syntax::ast::Ast; +use std::time; + +impl<'a> Arbitrary<'a> for Duration { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { + Ok(Self::from(time::Duration::new(u64::arbitrary(u)?, u32::arbitrary(u)?))) + } +} + +impl<'a> Arbitrary<'a> for Datetime { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { + let result = chrono::DateTime::UNIX_EPOCH + chrono::Duration::seconds(i64::arbitrary(u)?); + Ok(Self(result)) + } +} + +impl<'a> Arbitrary<'a> for Regex { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { + let ast = Ast::arbitrary(u)?; + Ok(Self( + regex::Regex::new(&format!("{ast}")).map_err(|_| arbitrary::Error::IncorrectFormat)?, + )) + } +} + +impl<'a> Arbitrary<'a> for ChangeFeed { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { + Ok(Self { + expiry: time::Duration::new(u64::arbitrary(u)?, u32::arbitrary(u)?), + }) + } +} + +impl<'a> Arbitrary<'a> for SleepStatement { + fn arbitrary(_u: &mut Unstructured<'a>) -> Result { + Ok(Self { + // When fuzzing we don't want to sleep, that's slow... we want insomnia. + duration: Duration(time::Duration::new(0, 0)), + }) + } +} diff --git a/core/src/sql/v2/array.rs b/core/src/sql/v2/array.rs new file mode 100644 index 00000000..51146dc3 --- /dev/null +++ b/core/src/sql/v2/array.rs @@ -0,0 +1,482 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::{ + fmt::{pretty_indent, Fmt, Pretty}, + Number, Operation, Value, +}; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::collections::HashSet; +use std::fmt::{self, Display, Formatter, Write}; +use std::ops; +use std::ops::Deref; +use std::ops::DerefMut; + +pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Array"; + +#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[serde(rename = "$surrealdb::private::sql::Array")] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Array(pub Vec); + +impl From for Array { + fn from(v: Value) -> Self { + vec![v].into() + } +} + +impl From> for Array { + fn from(v: Vec) -> Self { + Self(v) + } +} + +impl From> for Array { + fn from(v: Vec) -> Self { + Self(v.into_iter().map(Value::from).collect()) + } +} + +impl From> for Array { + fn from(v: Vec) -> Self { + Self(v.into_iter().map(Value::from).collect()) + } +} + +impl From> for Array { + fn from(v: Vec<&str>) -> Self { + Self(v.into_iter().map(Value::from).collect()) + } +} + +impl From> for Array { + fn from(v: Vec) -> Self { + Self(v.into_iter().map(Value::from).collect()) + } +} + +impl From> for Array { + fn from(v: Vec) -> Self { + Self(v.into_iter().map(Value::from).collect()) + } +} + +impl From> for Array { + fn from(v: Vec) -> Self { + Self(v.into_iter().map(Value::from).collect()) + } +} + +impl From> for Array { + fn from(v: Vec) -> Self { + Self(v.into_iter().map(Value::from).collect()) + } +} + +impl From for Vec { + fn from(s: Array) -> Self { + s.0 + } +} + +impl FromIterator for Array { + fn from_iter>(iter: I) -> Self { + Array(iter.into_iter().collect()) + } +} + +impl Deref for Array { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Array { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl IntoIterator for Array { + type Item = Value; + type IntoIter = std::vec::IntoIter; + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl Array { + // Create a new empty array + pub fn new() -> Self { + Self::default() + } + // Create a new array with capacity + pub fn with_capacity(len: usize) -> Self { + Self(Vec::with_capacity(len)) + } + // Get the length of the array + pub fn len(&self) -> usize { + self.0.len() + } + // Check if there array is empty + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } +} + +impl Array { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + ) -> Result { + let mut x = Self::with_capacity(self.len()); + for v in self.iter() { + match v.compute(ctx, opt, txn, doc).await { + Ok(v) => x.push(v), + Err(e) => return Err(e), + }; + } + Ok(Value::Array(x)) + } + + pub(crate) fn is_all_none_or_null(&self) -> bool { + self.0.iter().all(|v| v.is_none_or_null()) + } + + pub(crate) fn is_static(&self) -> bool { + self.iter().all(Value::is_static) + } +} + +impl Display for Array { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let mut f = Pretty::from(f); + f.write_char('[')?; + if !self.is_empty() { + let indent = pretty_indent(); + write!(f, "{}", Fmt::pretty_comma_separated(self.as_slice()))?; + drop(indent); + } + f.write_char(']') + } +} + +// ------------------------------ + +impl ops::Add for Array { + type Output = Self; + fn add(mut self, other: Value) -> Self { + self.0.push(other); + self + } +} + +impl ops::Add for Array { + type Output = Self; + fn add(mut self, mut other: Self) -> Self { + self.0.append(&mut other.0); + self + } +} + +// ------------------------------ + +impl ops::Sub for Array { + type Output = Self; + fn sub(mut self, other: Value) -> Self { + if let Some(p) = self.0.iter().position(|x| *x == other) { + self.0.remove(p); + } + self + } +} + +impl ops::Sub for Array { + type Output = Self; + fn sub(mut self, other: Self) -> Self { + for v in other.0 { + if let Some(p) = self.0.iter().position(|x| *x == v) { + self.0.remove(p); + } + } + self + } +} + +// ------------------------------ + +pub trait Abolish { + fn abolish(&mut self, f: F) + where + F: FnMut(usize) -> bool; +} + +impl Abolish for Vec { + fn abolish(&mut self, mut f: F) + where + F: FnMut(usize) -> bool, + { + let mut i = 0; + // FIXME: use drain_filter once stabilized (https://github.com/rust-lang/rust/issues/43244) + // to avoid negation of the predicate return value. + self.retain(|_| { + let retain = !f(i); + i += 1; + retain + }); + } +} + +// ------------------------------ + +pub(crate) trait Clump { + fn clump(self, clump_size: usize) -> T; +} + +impl Clump for Array { + fn clump(self, clump_size: usize) -> Array { + self.0 + .chunks(clump_size) + .map::(|chunk| chunk.to_vec().into()) + .collect::>() + .into() + } +} + +// ------------------------------ + +pub(crate) trait Combine { + fn combine(self, other: T) -> T; +} + +impl Combine for Array { + fn combine(self, other: Self) -> Array { + let mut out = Self::with_capacity(self.len().saturating_mul(other.len())); + for a in self.iter() { + for b in other.iter() { + out.push(vec![a.clone(), b.clone()].into()); + } + } + out + } +} + +// ------------------------------ + +pub(crate) trait Complement { + fn complement(self, other: T) -> T; +} + +impl Complement for Array { + fn complement(self, other: Self) -> Array { + let mut out = Array::new(); + for v in self.into_iter() { + if !other.contains(&v) { + out.push(v) + } + } + out + } +} + +// ------------------------------ + +pub(crate) trait Concat { + fn concat(self, other: T) -> T; +} + +impl Concat for Array { + fn concat(mut self, mut other: Array) -> Array { + self.append(&mut other); + self + } +} + +// ------------------------------ + +pub(crate) trait Difference { + fn difference(self, other: T) -> T; +} + +impl Difference for Array { + fn difference(self, mut other: Array) -> Array { + let mut out = Array::new(); + for v in self.into_iter() { + if let Some(pos) = other.iter().position(|w| v == *w) { + other.remove(pos); + } else { + out.push(v); + } + } + out.append(&mut other); + out + } +} + +// ------------------------------ + +pub(crate) trait Flatten { + fn flatten(self) -> T; +} + +impl Flatten for Array { + fn flatten(self) -> Array { + let mut out = Array::new(); + for v in self.into_iter() { + match v { + Value::Array(mut a) => out.append(&mut a), + _ => out.push(v), + } + } + out + } +} + +// ------------------------------ + +pub(crate) trait Intersect { + fn intersect(self, other: T) -> T; +} + +impl Intersect for Array { + fn intersect(self, mut other: Self) -> Self { + let mut out = Self::new(); + for v in self.0.into_iter() { + if let Some(pos) = other.iter().position(|w| v == *w) { + other.remove(pos); + out.push(v); + } + } + out + } +} + +// ------------------------------ + +// Documented with the assumption that it is just for arrays. +pub(crate) trait Matches { + /// Returns an array complimenting the original where each value is true or false + /// depending on whether it is == to the compared value. + /// + /// Admittedly, this is most often going to be used in `count(array::matches($arr, $val))` + /// to count the number of times an element appears in an array but it's nice to have + /// this in addition. + fn matches(self, compare_val: Value) -> T; +} + +impl Matches for Array { + fn matches(self, compare_val: Value) -> Array { + self.iter().map(|arr_val| (arr_val == &compare_val).into()).collect::>().into() + } +} + +// ------------------------------ + +// Documented with the assumption that it is just for arrays. +pub(crate) trait Transpose { + /// Stacks arrays on top of each other. This can serve as 2d array transposition. + /// + /// The input array can contain regular values which are treated as arrays with + /// a single element. + /// + /// It's best to think of the function as creating a layered structure of the arrays + /// rather than transposing them when the input is not a 2d array. See the examples + /// for what happense when the input arrays are not all the same size. + /// + /// Here's a diagram: + /// [0, 1, 2, 3], [4, 5, 6] + /// -> + /// [0 | 1 | 2 | 3] + /// [4 | 5 | 6 ] + /// ^ ^ ^ ^ + /// [0, 4] [1, 5] [2, 6] [3] + /// + /// # Examples + /// + /// ```ignore + /// fn array(sql: &str) -> Array { + /// unimplemented!(); + /// } + /// + /// // Example of `transpose` doing what it says on the tin. + /// assert_eq!(array("[[0, 1], [2, 3]]").transpose(), array("[[0, 2], [1, 3]]")); + /// // `transpose` can be thought of layering arrays on top of each other so when + /// // one array runs out, it stops appearing in the output. + /// assert_eq!(array("[[0, 1], [2]]").transpose(), array("[[0, 2], [1]]")); + /// assert_eq!(array("[0, 1, 2]").transpose(), array("[[0, 1, 2]]")); + /// ``` + fn transpose(self) -> T; +} + +impl Transpose for Array { + fn transpose(self) -> Array { + if self.is_empty() { + return self; + } + // I'm sure there's a way more efficient way to do this that I don't know about. + // The new array will be at *least* this large so we can start there; + let mut transposed_vec = Vec::::with_capacity(self.len()); + let mut iters = self + .iter() + .map(|v| { + if let Value::Array(arr) = v { + Box::new(arr.iter().cloned()) as Box> + } else { + Box::new(std::iter::once(v).cloned()) + as Box> + } + }) + .collect::>(); + // We know there is at least one element in the array therefore iters is not empty. + // This is safe. + let longest_length = iters.iter().map(|i| i.len()).max().unwrap(); + for _ in 0..longest_length { + transposed_vec + .push(iters.iter_mut().filter_map(|i| i.next()).collect::>().into()); + } + transposed_vec.into() + } +} + +// ------------------------------ + +pub(crate) trait Union { + fn union(self, other: T) -> T; +} + +impl Union for Array { + fn union(mut self, mut other: Self) -> Array { + self.append(&mut other); + self.uniq() + } +} + +// ------------------------------ + +pub(crate) trait Uniq { + fn uniq(self) -> T; +} + +impl Uniq for Array { + fn uniq(mut self) -> Array { + let mut set: HashSet<&Value> = HashSet::new(); + let mut to_remove: Vec = Vec::new(); + for (i, item) in self.iter().enumerate() { + if !set.insert(item) { + to_remove.push(i); + } + } + for i in to_remove.iter().rev() { + self.remove(*i); + } + self + } +} diff --git a/core/src/sql/v2/base.rs b/core/src/sql/v2/base.rs new file mode 100644 index 00000000..4afc476f --- /dev/null +++ b/core/src/sql/v2/base.rs @@ -0,0 +1,31 @@ +use crate::sql::Ident; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub enum Base { + Root, + Ns, + Db, + Sc(Ident), +} + +impl Default for Base { + fn default() -> Self { + Self::Root + } +} + +impl fmt::Display for Base { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Ns => f.write_str("NAMESPACE"), + Self::Db => f.write_str("DATABASE"), + Self::Sc(sc) => write!(f, "SCOPE {sc}"), + Self::Root => f.write_str("ROOT"), + } + } +} diff --git a/core/src/sql/v2/block.rs b/core/src/sql/v2/block.rs new file mode 100644 index 00000000..70f58a57 --- /dev/null +++ b/core/src/sql/v2/block.rs @@ -0,0 +1,242 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::fmt::{is_pretty, pretty_indent, Fmt, Pretty}; +use crate::sql::statements::{ + BreakStatement, ContinueStatement, CreateStatement, DefineStatement, DeleteStatement, + ForeachStatement, IfelseStatement, InsertStatement, OutputStatement, RelateStatement, + RemoveStatement, SelectStatement, SetStatement, ThrowStatement, UpdateStatement, +}; +use crate::sql::value::Value; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::cmp::Ordering; +use std::fmt::{self, Display, Formatter, Write}; +use std::ops::Deref; + +pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Block"; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[serde(rename = "$surrealdb::private::sql::Block")] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Block(pub Vec); + +impl Deref for Block { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From for Block { + fn from(v: Value) -> Self { + Block(vec![Entry::Value(v)]) + } +} + +impl Block { + /// Check if we require a writeable transaction + pub(crate) fn writeable(&self) -> bool { + self.iter().any(Entry::writeable) + } + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + ) -> Result { + // Duplicate context + let mut ctx = Context::new(ctx); + // Loop over the statements + for (i, v) in self.iter().enumerate() { + match v { + Entry::Set(v) => { + let val = v.compute(&ctx, opt, txn, doc).await?; + ctx.add_value(v.name.to_owned(), val); + } + Entry::Throw(v) => { + // Always errors immediately + v.compute(&ctx, opt, txn, doc).await?; + } + Entry::Break(v) => { + // Always errors immediately + v.compute(&ctx, opt, txn, doc).await?; + } + Entry::Continue(v) => { + // Always errors immediately + v.compute(&ctx, opt, txn, doc).await?; + } + Entry::Foreach(v) => { + v.compute(&ctx, opt, txn, doc).await?; + } + Entry::Ifelse(v) => { + v.compute(&ctx, opt, txn, doc).await?; + } + Entry::Select(v) => { + v.compute(&ctx, opt, txn, doc).await?; + } + Entry::Create(v) => { + v.compute(&ctx, opt, txn, doc).await?; + } + Entry::Update(v) => { + v.compute(&ctx, opt, txn, doc).await?; + } + Entry::Delete(v) => { + v.compute(&ctx, opt, txn, doc).await?; + } + Entry::Relate(v) => { + v.compute(&ctx, opt, txn, doc).await?; + } + Entry::Insert(v) => { + v.compute(&ctx, opt, txn, doc).await?; + } + Entry::Define(v) => { + v.compute(&ctx, opt, txn, doc).await?; + } + Entry::Remove(v) => { + v.compute(&ctx, opt, txn, doc).await?; + } + Entry::Output(v) => { + // Return the RETURN value + return v.compute(&ctx, opt, txn, doc).await; + } + Entry::Value(v) => { + if i == self.len() - 1 { + // If the last entry then return the value + return v.compute(&ctx, opt, txn, doc).await; + } else { + // Otherwise just process the value + v.compute(&ctx, opt, txn, doc).await?; + } + } + } + } + // Return nothing + Ok(Value::None) + } +} + +impl Display for Block { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let mut f = Pretty::from(f); + match (self.len(), self.first()) { + (0, _) => f.write_str("{}"), + (1, Some(Entry::Value(v))) => { + write!(f, "{{ {v} }}") + } + (l, _) => { + f.write_char('{')?; + if l > 1 { + f.write_char('\n')?; + } else if !is_pretty() { + f.write_char(' ')?; + } + let indent = pretty_indent(); + if is_pretty() { + write!( + f, + "{}", + &Fmt::two_line_separated( + self.0.iter().map(|args| Fmt::new(args, |v, f| write!(f, "{};", v))), + ) + )?; + } else { + write!( + f, + "{}", + &Fmt::one_line_separated( + self.0.iter().map(|args| Fmt::new(args, |v, f| write!(f, "{};", v))), + ) + )?; + } + drop(indent); + if l > 1 { + f.write_char('\n')?; + } else if !is_pretty() { + f.write_char(' ')?; + } + f.write_char('}') + } + } + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub enum Entry { + Value(Value), + Set(SetStatement), + Ifelse(IfelseStatement), + Select(SelectStatement), + Create(CreateStatement), + Update(UpdateStatement), + Delete(DeleteStatement), + Relate(RelateStatement), + Insert(InsertStatement), + Output(OutputStatement), + Define(DefineStatement), + Remove(RemoveStatement), + Throw(ThrowStatement), + Break(BreakStatement), + Continue(ContinueStatement), + Foreach(ForeachStatement), +} + +impl PartialOrd for Entry { + #[inline] + fn partial_cmp(&self, _: &Self) -> Option { + None + } +} + +impl Entry { + /// Check if we require a writeable transaction + pub(crate) fn writeable(&self) -> bool { + match self { + Self::Set(v) => v.writeable(), + Self::Value(v) => v.writeable(), + Self::Ifelse(v) => v.writeable(), + Self::Select(v) => v.writeable(), + Self::Create(v) => v.writeable(), + Self::Update(v) => v.writeable(), + Self::Delete(v) => v.writeable(), + Self::Relate(v) => v.writeable(), + Self::Insert(v) => v.writeable(), + Self::Output(v) => v.writeable(), + Self::Define(v) => v.writeable(), + Self::Remove(v) => v.writeable(), + Self::Throw(v) => v.writeable(), + Self::Break(v) => v.writeable(), + Self::Continue(v) => v.writeable(), + Self::Foreach(v) => v.writeable(), + } + } +} + +impl Display for Entry { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + Self::Set(v) => write!(f, "{v}"), + Self::Value(v) => Display::fmt(v, f), + Self::Ifelse(v) => write!(f, "{v}"), + Self::Select(v) => write!(f, "{v}"), + Self::Create(v) => write!(f, "{v}"), + Self::Update(v) => write!(f, "{v}"), + Self::Delete(v) => write!(f, "{v}"), + Self::Relate(v) => write!(f, "{v}"), + Self::Insert(v) => write!(f, "{v}"), + Self::Output(v) => write!(f, "{v}"), + Self::Define(v) => write!(f, "{v}"), + Self::Remove(v) => write!(f, "{v}"), + Self::Throw(v) => write!(f, "{v}"), + Self::Break(v) => write!(f, "{v}"), + Self::Continue(v) => write!(f, "{v}"), + Self::Foreach(v) => write!(f, "{v}"), + } + } +} diff --git a/core/src/sql/v2/bytes.rs b/core/src/sql/v2/bytes.rs new file mode 100644 index 00000000..8989e945 --- /dev/null +++ b/core/src/sql/v2/bytes.rs @@ -0,0 +1,95 @@ +use base64_lib::{engine::general_purpose::STANDARD_NO_PAD, Engine}; +use revision::revisioned; +use serde::{ + de::{self, Visitor}, + Deserialize, Serialize, +}; +use std::fmt::{self, Display, Formatter}; +use std::ops::Deref; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Bytes(pub(crate) Vec); + +impl Bytes { + pub fn into_inner(self) -> Vec { + self.0 + } +} + +impl From> for Bytes { + fn from(v: Vec) -> Self { + Self(v) + } +} + +impl Deref for Bytes { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Display for Bytes { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "encoding::base64::decode(\"{}\")", STANDARD_NO_PAD.encode(&self.0)) + } +} + +impl Serialize for Bytes { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_bytes(&self.0) + } +} + +impl<'de> Deserialize<'de> for Bytes { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct RawBytesVisitor; + + impl<'de> Visitor<'de> for RawBytesVisitor { + type Value = Bytes; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("bytes") + } + + fn visit_byte_buf(self, v: Vec) -> Result + where + E: de::Error, + { + Ok(Bytes(v)) + } + + fn visit_bytes(self, v: &[u8]) -> Result + where + E: de::Error, + { + Ok(Bytes(v.to_owned())) + } + } + + deserializer.deserialize_byte_buf(RawBytesVisitor) + } +} + +#[cfg(test)] +mod tests { + use crate::sql::{Bytes, Value}; + + #[test] + fn serialize() { + let val = Value::Bytes(Bytes(vec![1, 2, 3, 5])); + let serialized: Vec = val.clone().into(); + println!("{serialized:?}"); + let deserialized = Value::from(serialized); + assert_eq!(val, deserialized); + } +} diff --git a/core/src/sql/v2/cast.rs b/core/src/sql/v2/cast.rs new file mode 100644 index 00000000..df195297 --- /dev/null +++ b/core/src/sql/v2/cast.rs @@ -0,0 +1,53 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::{Idiom, Kind, Value}; +use async_recursion::async_recursion; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::cmp::Ordering; +use std::fmt; + +pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Cast"; + +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)] +#[serde(rename = "$surrealdb::private::sql::Cast")] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Cast(pub Kind, pub Value); + +impl PartialOrd for Cast { + #[inline] + fn partial_cmp(&self, _: &Self) -> Option { + None + } +} + +impl Cast { + /// Convert cast to a field name + pub fn to_idiom(&self) -> Idiom { + self.1.to_idiom() + } +} + +impl Cast { + #[cfg_attr(not(target_arch = "wasm32"), async_recursion)] + #[cfg_attr(target_arch = "wasm32", async_recursion(?Send))] + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&'async_recursion CursorDoc<'_>>, + ) -> Result { + // Compute the value to be cast and convert it + self.1.compute(ctx, opt, txn, doc).await?.convert_to(&self.0) + } +} + +impl fmt::Display for Cast { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "<{}> {}", self.0, self.1) + } +} diff --git a/core/src/sql/v2/changefeed.rs b/core/src/sql/v2/changefeed.rs new file mode 100644 index 00000000..e641d0d3 --- /dev/null +++ b/core/src/sql/v2/changefeed.rs @@ -0,0 +1,27 @@ +use crate::sql::duration::Duration; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Formatter}; +use std::str; +use std::time; + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[revisioned(revision = 1)] +pub struct ChangeFeed { + pub expiry: time::Duration, +} + +impl Display for ChangeFeed { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "CHANGEFEED {}", Duration(self.expiry))?; + Ok(()) + } +} + +impl Default for ChangeFeed { + fn default() -> Self { + Self { + expiry: time::Duration::from_secs(0), + } + } +} diff --git a/core/src/sql/v2/cond.rs b/core/src/sql/v2/cond.rs new file mode 100644 index 00000000..0cf86413 --- /dev/null +++ b/core/src/sql/v2/cond.rs @@ -0,0 +1,23 @@ +use crate::sql::value::Value; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; +use std::ops::Deref; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Cond(pub Value); + +impl Deref for Cond { + type Target = Value; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl fmt::Display for Cond { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "WHERE {}", self.0) + } +} diff --git a/core/src/sql/v2/constant.rs b/core/src/sql/v2/constant.rs new file mode 100644 index 00000000..c6cc28f2 --- /dev/null +++ b/core/src/sql/v2/constant.rs @@ -0,0 +1,119 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::value::Value; +use crate::sql::Datetime; +use chrono::TimeZone; +use chrono::Utc; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Constant"; + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[serde(rename = "$surrealdb::private::sql::Constant")] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub enum Constant { + MathE, + MathFrac1Pi, + MathFrac1Sqrt2, + MathFrac2Pi, + MathFrac2SqrtPi, + MathFracPi2, + MathFracPi3, + MathFracPi4, + MathFracPi6, + MathFracPi8, + MathInf, + MathLn10, + MathLn2, + MathLog102, + MathLog10E, + MathLog210, + MathLog2E, + MathPi, + MathSqrt2, + MathTau, + TimeEpoch, + // Add new variants here +} + +/// A type of constant that may be converted to a value or JSON. +pub(crate) enum ConstantValue { + Float(f64), + Datetime(Datetime), +} + +impl Constant { + pub(crate) fn value(&self) -> ConstantValue { + use std::f64::consts as f64c; + match self { + Self::MathE => ConstantValue::Float(f64c::E), + Self::MathFrac1Pi => ConstantValue::Float(f64c::FRAC_1_PI), + Self::MathFrac1Sqrt2 => ConstantValue::Float(f64c::FRAC_1_SQRT_2), + Self::MathFrac2Pi => ConstantValue::Float(f64c::FRAC_2_PI), + Self::MathFrac2SqrtPi => ConstantValue::Float(f64c::FRAC_2_SQRT_PI), + Self::MathFracPi2 => ConstantValue::Float(f64c::FRAC_PI_2), + Self::MathFracPi3 => ConstantValue::Float(f64c::FRAC_PI_3), + Self::MathFracPi4 => ConstantValue::Float(f64c::FRAC_PI_4), + Self::MathFracPi6 => ConstantValue::Float(f64c::FRAC_PI_6), + Self::MathFracPi8 => ConstantValue::Float(f64c::FRAC_PI_8), + Self::MathInf => ConstantValue::Float(f64::INFINITY), + Self::MathLn10 => ConstantValue::Float(f64c::LN_10), + Self::MathLn2 => ConstantValue::Float(f64c::LN_2), + Self::MathLog102 => ConstantValue::Float(f64c::LOG10_2), + Self::MathLog10E => ConstantValue::Float(f64c::LOG10_E), + Self::MathLog210 => ConstantValue::Float(f64c::LOG2_10), + Self::MathLog2E => ConstantValue::Float(f64c::LOG2_E), + Self::MathPi => ConstantValue::Float(f64c::PI), + Self::MathSqrt2 => ConstantValue::Float(f64c::SQRT_2), + Self::MathTau => ConstantValue::Float(f64c::TAU), + Self::TimeEpoch => ConstantValue::Datetime(Datetime(Utc.timestamp_nanos(0))), + } + } + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + _ctx: &Context<'_>, + _opt: &Options, + _txn: &Transaction, + _doc: Option<&CursorDoc<'_>>, + ) -> Result { + Ok(match self.value() { + ConstantValue::Datetime(d) => d.into(), + ConstantValue::Float(f) => f.into(), + }) + } +} + +impl fmt::Display for Constant { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(match self { + Self::MathE => "math::E", + Self::MathFrac1Pi => "math::FRAC_1_PI", + Self::MathFrac1Sqrt2 => "math::FRAC_1_SQRT_2", + Self::MathFrac2Pi => "math::FRAC_2_PI", + Self::MathFrac2SqrtPi => "math::FRAC_2_SQRT_PI", + Self::MathFracPi2 => "math::FRAC_PI_2", + Self::MathFracPi3 => "math::FRAC_PI_3", + Self::MathFracPi4 => "math::FRAC_PI_4", + Self::MathFracPi6 => "math::FRAC_PI_6", + Self::MathFracPi8 => "math::FRAC_PI_8", + Self::MathInf => "math::INF", + Self::MathLn10 => "math::LN_10", + Self::MathLn2 => "math::LN_2", + Self::MathLog102 => "math::LOG10_2", + Self::MathLog10E => "math::LOG10_E", + Self::MathLog210 => "math::LOG2_10", + Self::MathLog2E => "math::LOG2_E", + Self::MathPi => "math::PI", + Self::MathSqrt2 => "math::SQRT_2", + Self::MathTau => "math::TAU", + Self::TimeEpoch => "time::EPOCH", + }) + } +} diff --git a/core/src/sql/v2/data.rs b/core/src/sql/v2/data.rs new file mode 100644 index 00000000..85f9fa9a --- /dev/null +++ b/core/src/sql/v2/data.rs @@ -0,0 +1,109 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::err::Error; +use crate::sql::fmt::Fmt; +use crate::sql::idiom::Idiom; +use crate::sql::operator::Operator; +use crate::sql::value::Value; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Formatter}; + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub enum Data { + EmptyExpression, + SetExpression(Vec<(Idiom, Operator, Value)>), + UnsetExpression(Vec), + PatchExpression(Value), + MergeExpression(Value), + ReplaceExpression(Value), + ContentExpression(Value), + SingleExpression(Value), + ValuesExpression(Vec>), + UpdateExpression(Vec<(Idiom, Operator, Value)>), +} + +impl Default for Data { + fn default() -> Self { + Self::EmptyExpression + } +} + +impl Data { + /// Fetch the 'id' field if one has been specified + pub(crate) async fn rid( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + ) -> Result, Error> { + match self { + Self::MergeExpression(v) => { + // This MERGE expression has an 'id' field + Ok(v.compute(ctx, opt, txn, None).await?.rid().some()) + } + Self::ReplaceExpression(v) => { + // This REPLACE expression has an 'id' field + Ok(v.compute(ctx, opt, txn, None).await?.rid().some()) + } + Self::ContentExpression(v) => { + // This CONTENT expression has an 'id' field + Ok(v.compute(ctx, opt, txn, None).await?.rid().some()) + } + Self::SetExpression(v) => match v.iter().find(|f| f.0.is_id()) { + Some((_, _, v)) => { + // This SET expression has an 'id' field + Ok(v.compute(ctx, opt, txn, None).await?.some()) + } + // This SET expression had no 'id' field + _ => Ok(None), + }, + // Generate a random id for all other data clauses + _ => Ok(None), + } + } +} + +impl Display for Data { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + Self::EmptyExpression => Ok(()), + Self::SetExpression(v) => write!( + f, + "SET {}", + Fmt::comma_separated( + v.iter().map(|args| Fmt::new(args, |(l, o, r), f| write!(f, "{l} {o} {r}",))) + ) + ), + Self::UnsetExpression(v) => write!( + f, + "UNSET {}", + Fmt::comma_separated(v.iter().map(|args| Fmt::new(args, |l, f| write!(f, "{l}",)))) + ), + Self::PatchExpression(v) => write!(f, "PATCH {v}"), + Self::MergeExpression(v) => write!(f, "MERGE {v}"), + Self::ReplaceExpression(v) => write!(f, "REPLACE {v}"), + Self::ContentExpression(v) => write!(f, "CONTENT {v}"), + Self::SingleExpression(v) => Display::fmt(v, f), + Self::ValuesExpression(v) => write!( + f, + "({}) VALUES {}", + Fmt::comma_separated(v.first().unwrap().iter().map(|(v, _)| v)), + Fmt::comma_separated(v.iter().map(|v| Fmt::new(v, |v, f| write!( + f, + "({})", + Fmt::comma_separated(v.iter().map(|(_, v)| v)) + )))) + ), + Self::UpdateExpression(v) => write!( + f, + "ON DUPLICATE KEY UPDATE {}", + Fmt::comma_separated( + v.iter().map(|args| Fmt::new(args, |(l, o, r), f| write!(f, "{l} {o} {r}",))) + ) + ), + } + } +} diff --git a/core/src/sql/v2/datetime.rs b/core/src/sql/v2/datetime.rs new file mode 100644 index 00000000..a76a880e --- /dev/null +++ b/core/src/sql/v2/datetime.rs @@ -0,0 +1,99 @@ +use crate::sql::duration::Duration; +use crate::sql::strand::Strand; +use crate::syn; +use chrono::{DateTime, SecondsFormat, Utc}; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Formatter}; +use std::ops; +use std::ops::Deref; +use std::str; +use std::str::FromStr; + +use super::escape::quote_str; + +pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Datetime"; + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize, Hash)] +#[serde(rename = "$surrealdb::private::sql::Datetime")] +#[revisioned(revision = 1)] +pub struct Datetime(pub DateTime); + +impl Default for Datetime { + fn default() -> Self { + Self(Utc::now()) + } +} + +impl From> for Datetime { + fn from(v: DateTime) -> Self { + Self(v) + } +} + +impl From for DateTime { + fn from(x: Datetime) -> Self { + x.0 + } +} + +impl FromStr for Datetime { + type Err = (); + fn from_str(s: &str) -> Result { + Self::try_from(s) + } +} + +impl TryFrom for Datetime { + type Error = (); + fn try_from(v: String) -> Result { + Self::try_from(v.as_str()) + } +} + +impl TryFrom for Datetime { + type Error = (); + fn try_from(v: Strand) -> Result { + Self::try_from(v.as_str()) + } +} + +impl TryFrom<&str> for Datetime { + type Error = (); + fn try_from(v: &str) -> Result { + match syn::datetime_raw(v) { + Ok(v) => Ok(v), + _ => Err(()), + } + } +} + +impl Deref for Datetime { + type Target = DateTime; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Datetime { + /// Convert the Datetime to a raw String + pub fn to_raw(&self) -> String { + self.0.to_rfc3339_opts(SecondsFormat::AutoSi, true) + } +} + +impl Display for Datetime { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + Display::fmt("e_str(&self.to_raw()), f) + } +} + +impl ops::Sub for Datetime { + type Output = Duration; + fn sub(self, other: Self) -> Duration { + match (self.0 - other.0).to_std() { + Ok(d) => Duration::from(d), + Err(_) => Duration::default(), + } + } +} diff --git a/core/src/sql/v2/dir.rs b/core/src/sql/v2/dir.rs new file mode 100644 index 00000000..98b9df6d --- /dev/null +++ b/core/src/sql/v2/dir.rs @@ -0,0 +1,28 @@ +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub enum Dir { + In, + Out, + Both, +} + +impl Default for Dir { + fn default() -> Self { + Self::Both + } +} + +impl fmt::Display for Dir { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(match self { + Self::In => "<-", + Self::Out => "->", + Self::Both => "<->", + }) + } +} diff --git a/core/src/sql/v2/duration.rs b/core/src/sql/v2/duration.rs new file mode 100644 index 00000000..ab10d38e --- /dev/null +++ b/core/src/sql/v2/duration.rs @@ -0,0 +1,291 @@ +use crate::sql::datetime::Datetime; +use crate::sql::strand::Strand; +use crate::syn; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; +use std::iter::Sum; +use std::ops; +use std::ops::Deref; +use std::str::FromStr; +use std::time; + +pub(crate) static SECONDS_PER_YEAR: u64 = 365 * SECONDS_PER_DAY; +pub(crate) static SECONDS_PER_WEEK: u64 = 7 * SECONDS_PER_DAY; +pub(crate) static SECONDS_PER_DAY: u64 = 24 * SECONDS_PER_HOUR; +pub(crate) static SECONDS_PER_HOUR: u64 = 60 * SECONDS_PER_MINUTE; +pub(crate) static SECONDS_PER_MINUTE: u64 = 60; +pub(crate) static NANOSECONDS_PER_MILLISECOND: u32 = 1000000; +pub(crate) static NANOSECONDS_PER_MICROSECOND: u32 = 1000; + +pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Duration"; + +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[serde(rename = "$surrealdb::private::sql::Duration")] +#[revisioned(revision = 1)] +pub struct Duration(pub time::Duration); + +impl From for Duration { + fn from(v: time::Duration) -> Self { + Self(v) + } +} + +impl From for time::Duration { + fn from(s: Duration) -> Self { + s.0 + } +} + +impl FromStr for Duration { + type Err = (); + fn from_str(s: &str) -> Result { + Self::try_from(s) + } +} + +impl TryFrom for Duration { + type Error = (); + fn try_from(v: String) -> Result { + Self::try_from(v.as_str()) + } +} + +impl TryFrom for Duration { + type Error = (); + fn try_from(v: Strand) -> Result { + Self::try_from(v.as_str()) + } +} + +impl TryFrom<&str> for Duration { + type Error = (); + fn try_from(v: &str) -> Result { + match syn::duration(v) { + Ok(v) => Ok(v), + _ => Err(()), + } + } +} + +impl Deref for Duration { + type Target = time::Duration; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Duration { + /// Convert the Duration to a raw String + pub fn to_raw(&self) -> String { + self.to_string() + } + /// Get the total number of nanoseconds + pub fn nanos(&self) -> u128 { + self.0.as_nanos() + } + /// Get the total number of microseconds + pub fn micros(&self) -> u128 { + self.0.as_micros() + } + /// Get the total number of milliseconds + pub fn millis(&self) -> u128 { + self.0.as_millis() + } + /// Get the total number of seconds + pub fn secs(&self) -> u64 { + self.0.as_secs() + } + /// Get the total number of minutes + pub fn mins(&self) -> u64 { + self.0.as_secs() / SECONDS_PER_MINUTE + } + /// Get the total number of hours + pub fn hours(&self) -> u64 { + self.0.as_secs() / SECONDS_PER_HOUR + } + /// Get the total number of dats + pub fn days(&self) -> u64 { + self.0.as_secs() / SECONDS_PER_DAY + } + /// Get the total number of months + pub fn weeks(&self) -> u64 { + self.0.as_secs() / SECONDS_PER_WEEK + } + /// Get the total number of years + pub fn years(&self) -> u64 { + self.0.as_secs() / SECONDS_PER_YEAR + } + /// Create a duration from nanoseconds + pub fn from_nanos(nanos: u64) -> Duration { + time::Duration::from_nanos(nanos).into() + } + /// Create a duration from microseconds + pub fn from_micros(micros: u64) -> Duration { + time::Duration::from_micros(micros).into() + } + /// Create a duration from milliseconds + pub fn from_millis(millis: u64) -> Duration { + time::Duration::from_millis(millis).into() + } + /// Create a duration from seconds + pub fn from_secs(secs: u64) -> Duration { + time::Duration::from_secs(secs).into() + } + /// Create a duration from minutes + pub fn from_mins(mins: u64) -> Duration { + time::Duration::from_secs(mins * SECONDS_PER_MINUTE).into() + } + /// Create a duration from hours + pub fn from_hours(hours: u64) -> Duration { + time::Duration::from_secs(hours * SECONDS_PER_HOUR).into() + } + /// Create a duration from days + pub fn from_days(days: u64) -> Duration { + time::Duration::from_secs(days * SECONDS_PER_DAY).into() + } + /// Create a duration from weeks + pub fn from_weeks(days: u64) -> Duration { + time::Duration::from_secs(days * SECONDS_PER_WEEK).into() + } +} + +impl fmt::Display for Duration { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // Split up the duration + let secs = self.0.as_secs(); + let nano = self.0.subsec_nanos(); + // Ensure no empty output + if secs == 0 && nano == 0 { + return write!(f, "0ns"); + } + // Calculate the total years + let year = secs / SECONDS_PER_YEAR; + let secs = secs % SECONDS_PER_YEAR; + // Calculate the total weeks + let week = secs / SECONDS_PER_WEEK; + let secs = secs % SECONDS_PER_WEEK; + // Calculate the total days + let days = secs / SECONDS_PER_DAY; + let secs = secs % SECONDS_PER_DAY; + // Calculate the total hours + let hour = secs / SECONDS_PER_HOUR; + let secs = secs % SECONDS_PER_HOUR; + // Calculate the total minutes + let mins = secs / SECONDS_PER_MINUTE; + let secs = secs % SECONDS_PER_MINUTE; + // Calculate the total milliseconds + let msec = nano / NANOSECONDS_PER_MILLISECOND; + let nano = nano % NANOSECONDS_PER_MILLISECOND; + // Calculate the total microseconds + let usec = nano / NANOSECONDS_PER_MICROSECOND; + let nano = nano % NANOSECONDS_PER_MICROSECOND; + // Write the different parts + if year > 0 { + write!(f, "{year}y")?; + } + if week > 0 { + write!(f, "{week}w")?; + } + if days > 0 { + write!(f, "{days}d")?; + } + if hour > 0 { + write!(f, "{hour}h")?; + } + if mins > 0 { + write!(f, "{mins}m")?; + } + if secs > 0 { + write!(f, "{secs}s")?; + } + if msec > 0 { + write!(f, "{msec}ms")?; + } + if usec > 0 { + write!(f, "{usec}µs")?; + } + if nano > 0 { + write!(f, "{nano}ns")?; + } + Ok(()) + } +} + +impl ops::Add for Duration { + type Output = Self; + fn add(self, other: Self) -> Self { + match self.0.checked_add(other.0) { + Some(v) => Duration::from(v), + None => Duration::from(time::Duration::MAX), + } + } +} + +impl<'a, 'b> ops::Add<&'b Duration> for &'a Duration { + type Output = Duration; + fn add(self, other: &'b Duration) -> Duration { + match self.0.checked_add(other.0) { + Some(v) => Duration::from(v), + None => Duration::from(time::Duration::MAX), + } + } +} + +impl ops::Sub for Duration { + type Output = Self; + fn sub(self, other: Self) -> Self { + match self.0.checked_sub(other.0) { + Some(v) => Duration::from(v), + None => Duration::default(), + } + } +} + +impl<'a, 'b> ops::Sub<&'b Duration> for &'a Duration { + type Output = Duration; + fn sub(self, other: &'b Duration) -> Duration { + match self.0.checked_sub(other.0) { + Some(v) => Duration::from(v), + None => Duration::default(), + } + } +} + +impl ops::Add for Duration { + type Output = Datetime; + fn add(self, other: Datetime) -> Datetime { + match chrono::Duration::from_std(self.0) { + Ok(d) => Datetime::from(other.0 + d), + Err(_) => Datetime::default(), + } + } +} + +impl ops::Sub for Duration { + type Output = Datetime; + fn sub(self, other: Datetime) -> Datetime { + match chrono::Duration::from_std(self.0) { + Ok(d) => Datetime::from(other.0 - d), + Err(_) => Datetime::default(), + } + } +} + +impl Sum for Duration { + fn sum(iter: I) -> Duration + where + I: Iterator, + { + iter.fold(Duration::default(), |a, b| a + b) + } +} + +impl<'a> Sum<&'a Self> for Duration { + fn sum(iter: I) -> Duration + where + I: Iterator, + { + iter.fold(Duration::default(), |a, b| &a + b) + } +} diff --git a/core/src/sql/v2/edges.rs b/core/src/sql/v2/edges.rs new file mode 100644 index 00000000..c05b745b --- /dev/null +++ b/core/src/sql/v2/edges.rs @@ -0,0 +1,28 @@ +use crate::sql::dir::Dir; +use crate::sql::table::Tables; +use crate::sql::thing::Thing; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Edges"; + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[serde(rename = "$surrealdb::private::sql::Edges")] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct Edges { + pub dir: Dir, + pub from: Thing, + pub what: Tables, +} + +impl fmt::Display for Edges { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.what.len() { + 0 => write!(f, "{}{}?", self.from, self.dir,), + 1 => write!(f, "{}{}{}", self.from, self.dir, self.what), + _ => write!(f, "{}{}({})", self.from, self.dir, self.what), + } + } +} diff --git a/core/src/sql/v2/ending.rs b/core/src/sql/v2/ending.rs new file mode 100644 index 00000000..378382b0 --- /dev/null +++ b/core/src/sql/v2/ending.rs @@ -0,0 +1,131 @@ +use crate::sql::comment::comment; +use crate::sql::comment::{mightbespace, shouldbespace}; +use crate::sql::error::IResult; +use crate::sql::operator::{assigner, binary}; +use nom::branch::alt; +use nom::bytes::complete::tag; +use nom::bytes::complete::tag_no_case; +use nom::character::complete::char; +use nom::character::complete::multispace1; +use nom::combinator::peek; +use nom::combinator::{eof, value}; +use nom::sequence::preceded; + +pub fn number(i: &str) -> IResult<&str, ()> { + peek(alt(( + value((), multispace1), // 1 + 1 + value((), binary), // 1+1 + value((), assigner), // 1=1 + value((), comment), // 1/*comment*/ + value((), char(')')), // (1) + value((), char(']')), // a[1] + value((), char('}')), // {k: 1} + value((), char('"')), + value((), char('\'')), + value((), char(';')), // SET a = 1; + value((), char(',')), // [1, 2] + value((), tag("..")), // thing:1..2 + value((), eof), // SET a = 1 + )))(i) +} + +pub fn ident(i: &str) -> IResult<&str, ()> { + peek(alt(( + value((), multispace1), // a + 1 + value((), binary), // a+1 + value((), assigner), // a+=1 + value((), comment), // a/*comment*/ + value((), char(')')), // (a) + value((), char(']')), // foo[a] + value((), char('}')), // {k: a} + value((), char(';')), // SET k = a; + value((), char(',')), // [a, b] + value((), char('.')), // a.k + value((), char('…')), // a… + value((), char('[')), // a[0] + value((), eof), // SET k = a + )))(i) +} + +/// none, false, etc. +pub fn keyword(i: &str) -> IResult<&str, ()> { + peek(alt(( + value((), multispace1), // false || true + value((), binary), // false||true + value((), comment), // false/*comment*/ + value((), char(')')), // (false) + value((), char(']')), // [WHERE k = false] + value((), char('}')), // {k: false} + value((), char(';')), // SET a = false; + value((), char(',')), // [false, true] + value((), eof), // SET a = false + )))(i) +} + +pub fn duration(i: &str) -> IResult<&str, ()> { + peek(alt(( + value((), multispace1), + value((), binary), + value((), assigner), + value((), comment), + value((), char(')')), + value((), char(']')), + value((), char('}')), + value((), char(';')), + value((), char(',')), + value((), char('.')), + value((), eof), + )))(i) +} + +pub fn field(i: &str) -> IResult<&str, ()> { + peek(alt(( + value( + (), + preceded( + shouldbespace, + alt((tag_no_case("FROM"), tag_no_case("TIMEOUT"), tag_no_case("PARALLEL"))), + ), + ), + value((), char(';')), + value((), eof), + )))(i) +} + +pub fn subquery(i: &str) -> IResult<&str, ()> { + peek(alt(( + value((), preceded(shouldbespace, tag_no_case("THEN"))), + value((), preceded(shouldbespace, tag_no_case("ELSE"))), + value((), preceded(shouldbespace, tag_no_case("END"))), + |i| { + let (i, _) = mightbespace(i)?; + alt(( + value((), eof), + value((), char(';')), + value((), char(',')), + value((), char('}')), + value((), char(')')), + value((), char(']')), + ))(i) + }, + )))(i) +} + +pub fn query(i: &str) -> IResult<&str, ()> { + peek(alt(( + value((), preceded(shouldbespace, tag_no_case("THEN"))), + value((), preceded(shouldbespace, tag_no_case("ELSE"))), + value((), preceded(shouldbespace, tag_no_case("END"))), + |i| { + let (i, _) = mightbespace(i)?; + alt(( + value((), eof), + value((), char(';')), + value((), char(',')), + value((), char('}')), + value((), char(')')), + value((), char(']')), + ))(i) + }, + )))(i) +} diff --git a/core/src/sql/v2/escape.rs b/core/src/sql/v2/escape.rs new file mode 100644 index 00000000..5b60e28f --- /dev/null +++ b/core/src/sql/v2/escape.rs @@ -0,0 +1,154 @@ +use std::borrow::Cow; + +const SINGLE: char = '\''; + +const BRACKETL: char = '⟨'; +const BRACKETR: char = '⟩'; +const BRACKET_ESC: &str = r"\⟩"; + +const DOUBLE: char = '"'; +const DOUBLE_ESC: &str = r#"\""#; + +const BACKTICK: char = '`'; +const BACKTICK_ESC: &str = r"\`"; + +/// Quotes a string with single or double quotes: +/// - cat -> 'cat' +/// - cat's -> "cat's" +/// - cat's "toy" -> "cat's \"toy\"" +/// +/// Escapes / as // +#[inline] +pub fn quote_str(s: &str) -> String { + // Rough approximation of capacity, which may be exceeded + // if things must be escaped. + let mut ret = String::with_capacity(2 + s.len()); + + fn escape_into(into: &mut String, s: &str, escape_double: bool) { + // Based on internals of str::replace + let mut last_end = 0; + for (start, part) in s.match_indices(|c| c == '\\' || (c == DOUBLE && escape_double)) { + into.push_str(&s[last_end..start]); + into.push_str(if part == "\\" { + "\\\\" + } else { + DOUBLE_ESC + }); + last_end = start + part.len(); + } + into.push_str(&s[last_end..s.len()]); + } + + let quote = if s.contains(SINGLE) { + DOUBLE + } else { + SINGLE + }; + + ret.push(quote); + escape_into(&mut ret, s, quote == DOUBLE); + ret.push(quote); + ret +} + +#[inline] +pub fn quote_plain_str(s: &str) -> String { + #[cfg(not(feature = "experimental-parser"))] + { + if crate::syn::thing(s).is_ok() { + let mut ret = quote_str(s); + ret.insert(0, 's'); + return ret; + } + + let mut ret = quote_str(s); + // HACK: We need to prefix strands which look like records, uuids, or datetimes with an `s` + // otherwise the strands will parsed as a different type when parsed again. + // This is not required for the new parser. + // Because this only required for the old parse we just reference the partial parsers + // directly to avoid having to create a common interface between the old and new parser. + if crate::syn::v1::literal::uuid(&ret).is_ok() + || crate::syn::v1::literal::datetime(&ret).is_ok() + || crate::syn::thing(&ret).is_ok() + { + ret.insert(0, 's'); + } + ret + } + + #[cfg(feature = "experimental-parser")] + quote_str(s) +} + +#[inline] +/// Escapes a key if necessary +pub fn escape_key(s: &str) -> Cow<'_, str> { + escape_normal(s, DOUBLE, DOUBLE, DOUBLE_ESC) +} + +#[inline] +/// Escapes an id if necessary +pub fn escape_rid(s: &str) -> Cow<'_, str> { + escape_numeric(s, BRACKETL, BRACKETR, BRACKET_ESC) +} + +#[inline] +/// Escapes an ident if necessary +pub fn escape_ident(s: &str) -> Cow<'_, str> { + escape_numeric(s, BACKTICK, BACKTICK, BACKTICK_ESC) +} + +#[inline] +pub fn escape_normal<'a>(s: &'a str, l: char, r: char, e: &str) -> Cow<'a, str> { + // Loop over each character + for x in s.bytes() { + // Check if character is allowed + if !(x.is_ascii_alphanumeric() || x == b'_') { + return Cow::Owned(format!("{l}{}{r}", s.replace(r, e))); + } + } + // Output the value + Cow::Borrowed(s) +} + +#[cfg(not(feature = "experimental-parser"))] +#[inline] +pub fn escape_numeric<'a>(s: &'a str, l: char, r: char, e: &str) -> Cow<'a, str> { + // Presume this is numeric + let mut numeric = true; + // Loop over each character + for x in s.bytes() { + // Check if character is allowed + if !(x.is_ascii_alphanumeric() || x == b'_') { + return Cow::Owned(format!("{l}{}{r}", s.replace(r, e))); + } + // Check if character is non-numeric + if !x.is_ascii_digit() { + numeric = false; + } + } + // Output the id value + match numeric { + // This is numeric so escape it + true => Cow::Owned(format!("{l}{}{r}", s.replace(r, e))), + // No need to escape the value + _ => Cow::Borrowed(s), + } +} + +#[cfg(feature = "experimental-parser")] +#[inline] +pub fn escape_numeric<'a>(s: &'a str, l: char, r: char, e: &str) -> Cow<'a, str> { + // Loop over each character + for (idx, x) in s.bytes().enumerate() { + // the first character is not allowed to be a digit. + if idx == 0 && x.is_ascii_digit() { + return Cow::Owned(format!("{l}{}{r}", s.replace(r, e))); + } + // Check if character is allowed + if !(x.is_ascii_alphanumeric() || x == b'_') { + return Cow::Owned(format!("{l}{}{r}", s.replace(r, e))); + } + } + Cow::Borrowed(s) +} diff --git a/core/src/sql/v2/explain.rs b/core/src/sql/v2/explain.rs new file mode 100644 index 00000000..4f73551e --- /dev/null +++ b/core/src/sql/v2/explain.rs @@ -0,0 +1,18 @@ +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Explain(pub bool); + +impl fmt::Display for Explain { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("EXPLAIN")?; + if self.0 { + f.write_str(" FULL")?; + } + Ok(()) + } +} diff --git a/core/src/sql/v2/expression.rs b/core/src/sql/v2/expression.rs new file mode 100644 index 00000000..20d5dd31 --- /dev/null +++ b/core/src/sql/v2/expression.rs @@ -0,0 +1,208 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::fnc; +use crate::sql::operator::Operator; +use crate::sql::value::Value; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; +use std::str; + +pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Expression"; + +/// Binary expressions. +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[serde(rename = "$surrealdb::private::sql::Expression")] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub enum Expression { + Unary { + o: Operator, + v: Value, + }, + Binary { + l: Value, + o: Operator, + r: Value, + }, +} + +impl Default for Expression { + fn default() -> Expression { + Expression::Binary { + l: Value::Null, + o: Operator::default(), + r: Value::Null, + } + } +} + +impl Expression { + /// Create a new binary expression + #[doc(hidden)] + pub fn new(l: Value, o: Operator, r: Value) -> Self { + Self::Binary { + l, + o, + r, + } + } +} + +impl Expression { + pub(crate) fn writeable(&self) -> bool { + match self { + Self::Unary { + v, + .. + } => v.writeable(), + Self::Binary { + l, + r, + .. + } => l.writeable() || r.writeable(), + } + } + + pub(crate) fn is_static(&self) -> bool { + match self { + Self::Unary { + v, + .. + } => v.is_static(), + Self::Binary { + l, + r, + .. + } => l.is_static() && r.is_static(), + } + } + + /// Returns the operator + pub(crate) fn operator(&self) -> &Operator { + match self { + Expression::Unary { + o, + .. + } => o, + Expression::Binary { + o, + .. + } => o, + } + } + + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + ) -> Result { + let (l, o, r) = match self { + Self::Unary { + o, + v, + } => { + let operand = v.compute(ctx, opt, txn, doc).await?; + return match o { + Operator::Neg => fnc::operate::neg(operand), + // TODO: Check if it is a number? + Operator::Add => Ok(operand), + Operator::Not => fnc::operate::not(operand), + op => unreachable!("{op:?} is not a unary op"), + }; + } + Self::Binary { + l, + o, + r, + } => (l, o, r), + }; + + let l = l.compute(ctx, opt, txn, doc).await?; + match o { + Operator::Or => { + if let true = l.is_truthy() { + return Ok(l); + } + } + Operator::And => { + if let false = l.is_truthy() { + return Ok(l); + } + } + Operator::Tco => { + if let true = l.is_truthy() { + return Ok(l); + } + } + Operator::Nco => { + if let true = l.is_some() { + return Ok(l); + } + } + _ => {} // Continue + } + let r = r.compute(ctx, opt, txn, doc).await?; + match o { + Operator::Or => fnc::operate::or(l, r), + Operator::And => fnc::operate::and(l, r), + Operator::Tco => fnc::operate::tco(l, r), + Operator::Nco => fnc::operate::nco(l, r), + Operator::Add => fnc::operate::add(l, r), + Operator::Sub => fnc::operate::sub(l, r), + Operator::Mul => fnc::operate::mul(l, r), + Operator::Div => fnc::operate::div(l, r), + Operator::Rem => fnc::operate::rem(l, r), + Operator::Pow => fnc::operate::pow(l, r), + Operator::Equal => fnc::operate::equal(&l, &r), + Operator::Exact => fnc::operate::exact(&l, &r), + Operator::NotEqual => fnc::operate::not_equal(&l, &r), + Operator::AllEqual => fnc::operate::all_equal(&l, &r), + Operator::AnyEqual => fnc::operate::any_equal(&l, &r), + Operator::Like => fnc::operate::like(&l, &r), + Operator::NotLike => fnc::operate::not_like(&l, &r), + Operator::AllLike => fnc::operate::all_like(&l, &r), + Operator::AnyLike => fnc::operate::any_like(&l, &r), + Operator::LessThan => fnc::operate::less_than(&l, &r), + Operator::LessThanOrEqual => fnc::operate::less_than_or_equal(&l, &r), + Operator::MoreThan => fnc::operate::more_than(&l, &r), + Operator::MoreThanOrEqual => fnc::operate::more_than_or_equal(&l, &r), + Operator::Contain => fnc::operate::contain(&l, &r), + Operator::NotContain => fnc::operate::not_contain(&l, &r), + Operator::ContainAll => fnc::operate::contain_all(&l, &r), + Operator::ContainAny => fnc::operate::contain_any(&l, &r), + Operator::ContainNone => fnc::operate::contain_none(&l, &r), + Operator::Inside => fnc::operate::inside(&l, &r), + Operator::NotInside => fnc::operate::not_inside(&l, &r), + Operator::AllInside => fnc::operate::inside_all(&l, &r), + Operator::AnyInside => fnc::operate::inside_any(&l, &r), + Operator::NoneInside => fnc::operate::inside_none(&l, &r), + Operator::Outside => fnc::operate::outside(&l, &r), + Operator::Intersects => fnc::operate::intersects(&l, &r), + Operator::Matches(_) => fnc::operate::matches(ctx, txn, doc, self).await, + Operator::Knn(_, _) => fnc::operate::knn(ctx, opt, txn, doc, self).await, + _ => unreachable!(), + } + } +} + +impl fmt::Display for Expression { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Unary { + o, + v, + } => write!(f, "{o}{v}"), + Self::Binary { + l, + o, + r, + } => write!(f, "{l} {o} {r}"), + } + } +} diff --git a/core/src/sql/v2/fetch.rs b/core/src/sql/v2/fetch.rs new file mode 100644 index 00000000..6038c4c4 --- /dev/null +++ b/core/src/sql/v2/fetch.rs @@ -0,0 +1,50 @@ +use crate::sql::fmt::Fmt; +use crate::sql::idiom::Idiom; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Formatter}; +use std::ops::Deref; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Fetchs(pub Vec); + +impl Deref for Fetchs { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl IntoIterator for Fetchs { + type Item = Fetch; + type IntoIter = std::vec::IntoIter; + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl fmt::Display for Fetchs { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "FETCH {}", Fmt::comma_separated(&self.0)) + } +} + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Fetch(pub Idiom); + +impl Deref for Fetch { + type Target = Idiom; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Display for Fetch { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + Display::fmt(&self.0, f) + } +} diff --git a/core/src/sql/v2/field.rs b/core/src/sql/v2/field.rs new file mode 100644 index 00000000..5134c570 --- /dev/null +++ b/core/src/sql/v2/field.rs @@ -0,0 +1,271 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::{fmt::Fmt, Idiom, Part, Value}; +use crate::syn; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::borrow::Cow; +use std::fmt::{self, Display, Formatter, Write}; +use std::ops::Deref; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Fields(pub Vec, pub bool); + +impl Fields { + pub fn all() -> Self { + Self(vec![Field::All], false) + } + /// Check to see if this field is a * projection + pub fn is_all(&self) -> bool { + self.0.iter().any(|v| matches!(v, Field::All)) + } + /// Get all fields which are not an * projection + pub fn other(&self) -> impl Iterator { + self.0.iter().filter(|v| !matches!(v, Field::All)) + } + /// Check to see if this field is a single VALUE clause + pub fn single(&self) -> Option<&Field> { + match (self.0.len(), self.1) { + (1, true) => match self.0.first() { + Some(Field::All) => None, + Some(v) => Some(v), + _ => None, + }, + _ => None, + } + } +} + +impl Deref for Fields { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl IntoIterator for Fields { + type Item = Field; + type IntoIter = std::vec::IntoIter; + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl Display for Fields { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self.single() { + Some(v) => write!(f, "VALUE {}", &v), + None => Display::fmt(&Fmt::comma_separated(&self.0), f), + } + } +} + +impl Fields { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + group: bool, + ) -> Result { + if let Some(doc) = doc { + self.compute_value(ctx, opt, txn, doc, group).await + } else { + let doc = (&Value::None).into(); + self.compute_value(ctx, opt, txn, &doc, group).await + } + } + + async fn compute_value( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: &CursorDoc<'_>, + group: bool, + ) -> Result { + // Ensure futures are run + let opt = &opt.new_with_futures(true); + // Process the desired output + let mut out = match self.is_all() { + true => doc.doc.compute(ctx, opt, txn, Some(doc)).await?, + false => Value::base(), + }; + for v in self.other() { + match v { + Field::All => (), + Field::Single { + expr, + alias, + } => { + let name = alias + .as_ref() + .map(Cow::Borrowed) + .unwrap_or_else(|| Cow::Owned(expr.to_idiom())); + match expr { + // This expression is a grouped aggregate function + Value::Function(f) if group && f.is_aggregate() => { + let x = match f.args().len() { + // If no function arguments, then compute the result + 0 => f.compute(ctx, opt, txn, Some(doc)).await?, + // If arguments, then pass the first value through + _ => f.args()[0].compute(ctx, opt, txn, Some(doc)).await?, + }; + // Check if this is a single VALUE field expression + match self.single().is_some() { + false => out.set(ctx, opt, txn, name.as_ref(), x).await?, + true => out = x, + } + } + // This expression is a multi-output graph traversal + Value::Idiom(v) if v.is_multi_yield() => { + // Store the different output yields here + let mut res: Vec<(&[Part], Value)> = Vec::new(); + // Split the expression by each output alias + for v in v.split_inclusive(Idiom::split_multi_yield) { + // Use the last fetched value for each fetch + let x = match res.last() { + Some((_, r)) => r, + None => doc.doc.as_ref(), + }; + // Continue fetching the next idiom part + let x = x + .get(ctx, opt, txn, Some(doc), v) + .await? + .compute(ctx, opt, txn, Some(doc)) + .await? + .flatten(); + // Add the result to the temporary store + res.push((v, x)); + } + // Assign each fetched yield to the output + for (p, x) in res { + match p.last().unwrap().alias() { + // This is an alias expression part + Some(a) => { + if let Some(i) = alias { + out.set(ctx, opt, txn, i, x.clone()).await?; + } + out.set(ctx, opt, txn, a, x).await?; + } + // This is the end of the expression + None => { + out.set(ctx, opt, txn, alias.as_ref().unwrap_or(v), x) + .await? + } + } + } + } + // This expression is a variable fields expression + Value::Function(f) if f.name() == Some("type::fields") => { + // Process the function using variable field projections + let expr = expr.compute(ctx, opt, txn, Some(doc)).await?; + // Check if this is a single VALUE field expression + match self.single().is_some() { + false => { + // Get the first argument which is guaranteed to exist + let args = match f.args().first().unwrap() { + Value::Param(v) => { + v.compute(ctx, opt, txn, Some(doc)).await? + } + v => v.to_owned(), + }; + // This value is always an array, so we can convert it + let expr: Vec = expr.try_into()?; + // This value is always an array, so we can convert it + let args: Vec = args.try_into()?; + // This value is always an array, so we can convert it + for (name, expr) in args.into_iter().zip(expr) { + // This value is always a string, so we can convert it + let name = syn::idiom(&name.to_raw_string())?; + // Check if this is a single VALUE field expression + out.set(ctx, opt, txn, name.as_ref(), expr).await? + } + } + true => out = expr, + } + } + // This expression is a variable field expression + Value::Function(f) if f.name() == Some("type::field") => { + // Process the function using variable field projections + let expr = expr.compute(ctx, opt, txn, Some(doc)).await?; + // Check if this is a single VALUE field expression + match self.single().is_some() { + false => { + // Get the first argument which is guaranteed to exist + let name = match f.args().first().unwrap() { + Value::Param(v) => { + v.compute(ctx, opt, txn, Some(doc)).await? + } + v => v.to_owned(), + }; + // find the name for the field, either from the argument or the + // alias. + let name = if let Some(x) = alias.as_ref().map(Cow::Borrowed) { + x + } else { + Cow::Owned(syn::idiom(&name.to_raw_string())?) + }; + // Add the projected field to the output document + out.set(ctx, opt, txn, name.as_ref(), expr).await? + } + true => out = expr, + } + } + // This expression is a normal field expression + _ => { + let expr = expr.compute(ctx, opt, txn, Some(doc)).await?; + // Check if this is a single VALUE field expression + match self.single().is_some() { + false => out.set(ctx, opt, txn, name.as_ref(), expr).await?, + true => out = expr, + } + } + } + } + } + } + Ok(out) + } +} + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub enum Field { + /// The `*` in `SELECT * FROM ...` + #[default] + All, + /// The 'rating' in `SELECT rating FROM ...` + Single { + expr: Value, + /// The `quality` in `SELECT rating AS quality FROM ...` + alias: Option, + }, +} + +impl Display for Field { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + Self::All => f.write_char('*'), + Self::Single { + expr, + alias, + } => { + Display::fmt(expr, f)?; + if let Some(alias) = alias { + f.write_str(" AS ")?; + Display::fmt(alias, f) + } else { + Ok(()) + } + } + } + } +} diff --git a/core/src/sql/v2/filter.rs b/core/src/sql/v2/filter.rs new file mode 100644 index 00000000..eeb285d8 --- /dev/null +++ b/core/src/sql/v2/filter.rs @@ -0,0 +1,30 @@ +use crate::sql::language::Language; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; +use std::fmt::Display; + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub enum Filter { + Ascii, + EdgeNgram(u16, u16), + Lowercase, + Ngram(u16, u16), + Snowball(Language), + Uppercase, +} + +impl Display for Filter { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Ascii => f.write_str("ASCII"), + Self::EdgeNgram(min, max) => write!(f, "EDGENGRAM({},{})", min, max), + Self::Lowercase => f.write_str("LOWERCASE"), + Self::Ngram(min, max) => write!(f, "NGRAM({},{})", min, max), + Self::Snowball(lang) => write!(f, "SNOWBALL({})", lang), + Self::Uppercase => f.write_str("UPPERCASE"), + } + } +} diff --git a/core/src/sql/v2/fmt.rs b/core/src/sql/v2/fmt.rs new file mode 100644 index 00000000..ba57abef --- /dev/null +++ b/core/src/sql/v2/fmt.rs @@ -0,0 +1,313 @@ +use std::cell::Cell; +use std::fmt::{self, Display, Formatter, Write}; +use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; + +/// Implements fmt::Display by calling formatter on contents. +pub(crate) struct Fmt { + contents: Cell>, + formatter: F, +} + +impl fmt::Result> Fmt { + pub(crate) fn new(t: T, formatter: F) -> Self { + Self { + contents: Cell::new(Some(t)), + formatter, + } + } +} + +impl fmt::Result> Display for Fmt { + /// fmt is single-use only. + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let contents = self.contents.replace(None).expect("only call Fmt::fmt once"); + (self.formatter)(contents, f) + } +} + +impl, T: Display> Fmt fmt::Result> { + /// Formats values with a comma and a space separating them. + pub(crate) fn comma_separated(into_iter: I) -> Self { + Self::new(into_iter, fmt_comma_separated) + } + + /// Formats values with a verbar and a space separating them. + pub(crate) fn verbar_separated(into_iter: I) -> Self { + Self::new(into_iter, fmt_verbar_separated) + } + + /// Formats values with a comma and a space separating them or, if pretty printing is in + /// effect, a comma, a newline, and indentation. + pub(crate) fn pretty_comma_separated(into_iter: I) -> Self { + Self::new(into_iter, fmt_pretty_comma_separated) + } + + /// Formats values with a new line separating them. + pub(crate) fn one_line_separated(into_iter: I) -> Self { + Self::new(into_iter, fmt_one_line_separated) + } + + /// Formats values with a new line separating them. + pub(crate) fn two_line_separated(into_iter: I) -> Self { + Self::new(into_iter, fmt_two_line_separated) + } +} + +fn fmt_comma_separated>( + into_iter: I, + f: &mut Formatter, +) -> fmt::Result { + for (i, v) in into_iter.into_iter().enumerate() { + if i > 0 { + f.write_str(", ")?; + } + Display::fmt(&v, f)?; + } + Ok(()) +} + +fn fmt_verbar_separated>( + into_iter: I, + f: &mut Formatter, +) -> fmt::Result { + for (i, v) in into_iter.into_iter().enumerate() { + if i > 0 { + f.write_str(" | ")?; + } + Display::fmt(&v, f)?; + } + Ok(()) +} + +fn fmt_pretty_comma_separated>( + into_iter: I, + f: &mut Formatter, +) -> fmt::Result { + for (i, v) in into_iter.into_iter().enumerate() { + if i > 0 { + if is_pretty() { + f.write_char(',')?; + pretty_sequence_item(); + } else { + f.write_str(", ")?; + } + } + Display::fmt(&v, f)?; + } + Ok(()) +} + +fn fmt_one_line_separated>( + into_iter: I, + f: &mut Formatter, +) -> fmt::Result { + for (i, v) in into_iter.into_iter().enumerate() { + if i > 0 { + if is_pretty() { + pretty_sequence_item(); + } else { + f.write_char('\n')?; + } + } + Display::fmt(&v, f)?; + } + Ok(()) +} + +fn fmt_two_line_separated>( + into_iter: I, + f: &mut Formatter, +) -> fmt::Result { + for (i, v) in into_iter.into_iter().enumerate() { + if i > 0 { + if is_pretty() { + f.write_char('\n')?; + pretty_sequence_item(); + } else { + f.write_char('\n')?; + f.write_char('\n')?; + } + } + Display::fmt(&v, f)?; + } + Ok(()) +} + +/// Creates a formatting function that joins iterators with an arbitrary separator. +pub fn fmt_separated_by>( + separator: impl Display, +) -> impl Fn(I, &mut Formatter) -> fmt::Result { + move |into_iter: I, f: &mut Formatter| { + for (i, v) in into_iter.into_iter().enumerate() { + if i > 0 { + Display::fmt(&separator, f)?; + } + Display::fmt(&v, f)?; + } + Ok(()) + } +} + +thread_local! { + // Avoid `RefCell`/`UnsafeCell` by using atomic types. Access is synchronized due to + // `thread_local!` so all accesses can use `Ordering::Relaxed`. + + /// Whether pretty-printing. + static PRETTY: AtomicBool = AtomicBool::new(false); + /// The current level of indentation, in units of tabs. + static INDENT: AtomicU32 = AtomicU32::new(0); + /// Whether the next formatting action should be preceded by a newline and indentation. + static NEW_LINE: AtomicBool = AtomicBool::new(false); +} + +/// An adapter that, if enabled, adds pretty print formatting. +pub(crate) struct Pretty { + inner: W, + /// This is the active pretty printer, responsible for injecting formatting. + active: bool, +} + +impl Pretty { + #[allow(unused)] + pub fn new(inner: W) -> Self { + Self::conditional(inner, true) + } + + pub fn conditional(inner: W, enable: bool) -> Self { + let pretty_started_here = enable + && PRETTY.with(|pretty| { + // Evaluates to true if PRETTY was false and is now true. + pretty.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed).is_ok() + }); + if pretty_started_here { + // Clean slate. + NEW_LINE.with(|new_line| new_line.store(false, Ordering::Relaxed)); + INDENT.with(|indent| indent.store(0, Ordering::Relaxed)); + } + Self { + inner, + // Don't want multiple active pretty printers, although they wouldn't necessarily misbehave. + active: pretty_started_here, + } + } +} + +impl<'a, 'b> From<&'a mut Formatter<'b>> for Pretty<&'a mut Formatter<'b>> { + fn from(f: &'a mut Formatter<'b>) -> Self { + Self::conditional(f, f.alternate()) + } +} + +impl Drop for Pretty { + fn drop(&mut self) { + if self.active { + PRETTY.with(|pretty| { + debug_assert!(pretty.load(Ordering::Relaxed), "pretty status changed unexpectedly"); + pretty.store(false, Ordering::Relaxed); + }); + } + } +} + +/// Returns whether pretty printing is in effect. +pub(crate) fn is_pretty() -> bool { + PRETTY.with(|pretty| pretty.load(Ordering::Relaxed)) +} + +/// If pretty printing is in effect, increments the indentation level (until the return value +/// is dropped). +#[must_use = "hold for the span of the indent, then drop"] +pub(crate) fn pretty_indent() -> PrettyGuard { + PrettyGuard::new(1) +} + +/// Marks the end of an item in the sequence, after which indentation will follow if pretty printing +/// is in effect. +pub(crate) fn pretty_sequence_item() { + // List items need a new line, but no additional indentation. + NEW_LINE.with(|new_line| new_line.store(true, Ordering::Relaxed)); +} + +/// When dropped, applies the opposite increment to the current indentation level. +pub(crate) struct PrettyGuard { + increment: i8, +} + +impl PrettyGuard { + fn new(increment: i8) -> Self { + Self::raw(increment); + PrettyGuard { + increment, + } + } + + fn raw(increment: i8) { + INDENT.with(|indent| { + // Equivalent to `indent += increment` if signed numbers could be added to unsigned + // numbers in stable, atomic Rust. + if increment >= 0 { + indent.fetch_add(increment as u32, Ordering::Relaxed); + } else { + indent.fetch_sub(increment.unsigned_abs() as u32, Ordering::Relaxed); + } + }); + NEW_LINE.with(|new_line| new_line.store(true, Ordering::Relaxed)); + } +} + +impl Drop for PrettyGuard { + fn drop(&mut self) { + Self::raw(-self.increment) + } +} + +impl std::fmt::Write for Pretty { + fn write_str(&mut self, s: &str) -> std::fmt::Result { + if self.active && NEW_LINE.with(|new_line| new_line.swap(false, Ordering::Relaxed)) { + // Newline. + self.inner.write_char('\n')?; + for _ in 0..INDENT.with(|indent| indent.load(Ordering::Relaxed)) { + // One level of indentation. + self.inner.write_char('\t')?; + } + } + // What we were asked to write. + self.inner.write_str(s) + } +} + +#[cfg(test)] +mod tests { + use crate::syn::{parse, value}; + + #[test] + fn pretty_query() { + let query = parse("SELECT * FROM {foo: [1, 2, 3]};").unwrap(); + assert_eq!(format!("{}", query), "SELECT * FROM { foo: [1, 2, 3] };"); + assert_eq!( + format!("{:#}", query), + "SELECT * FROM {\n\tfoo: [\n\t\t1,\n\t\t2,\n\t\t3\n\t]\n};" + ); + } + + #[test] + fn pretty_define_query() { + let query = parse("DEFINE TABLE test SCHEMAFULL PERMISSIONS FOR create, update, delete NONE FOR select WHERE public = true;").unwrap(); + assert_eq!(format!("{}", query), "DEFINE TABLE test SCHEMAFULL PERMISSIONS FOR select WHERE public = true, FOR create, update, delete NONE;"); + assert_eq!(format!("{:#}", query), "DEFINE TABLE test SCHEMAFULL\n\tPERMISSIONS\n\t\tFOR select\n\t\t\tWHERE public = true\n\t\tFOR create, update, delete NONE\n;"); + } + + #[test] + fn pretty_value() { + let value = value("{foo: [1, 2, 3]}").unwrap(); + assert_eq!(format!("{}", value), "{ foo: [1, 2, 3] }"); + assert_eq!(format!("{:#}", value), "{\n\tfoo: [\n\t\t1,\n\t\t2,\n\t\t3\n\t]\n}"); + } + + #[test] + fn pretty_array() { + let array = value("[1, 2, 3]").unwrap(); + assert_eq!(format!("{}", array), "[1, 2, 3]"); + assert_eq!(format!("{:#}", array), "[\n\t1,\n\t2,\n\t3\n]"); + } +} diff --git a/core/src/sql/v2/function.rs b/core/src/sql/v2/function.rs new file mode 100644 index 00000000..e2380ee5 --- /dev/null +++ b/core/src/sql/v2/function.rs @@ -0,0 +1,266 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::fnc; +use crate::iam::Action; +use crate::sql::fmt::Fmt; +use crate::sql::idiom::Idiom; +use crate::sql::script::Script; +use crate::sql::value::Value; +use crate::sql::Permission; +use async_recursion::async_recursion; +use futures::future::try_join_all; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::cmp::Ordering; +use std::fmt; + +use super::Kind; + +pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Function"; + +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)] +#[serde(rename = "$surrealdb::private::sql::Function")] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub enum Function { + Normal(String, Vec), + Custom(String, Vec), + Script(Script, Vec), + // Add new variants here +} + +impl PartialOrd for Function { + #[inline] + fn partial_cmp(&self, _: &Self) -> Option { + None + } +} + +impl Function { + /// Get function name if applicable + pub fn name(&self) -> Option<&str> { + match self { + Self::Normal(n, _) => Some(n.as_str()), + Self::Custom(n, _) => Some(n.as_str()), + _ => None, + } + } + /// Get function arguments if applicable + pub fn args(&self) -> &[Value] { + match self { + Self::Normal(_, a) => a, + Self::Custom(_, a) => a, + _ => &[], + } + } + /// Convert function call to a field name + pub fn to_idiom(&self) -> Idiom { + match self { + Self::Script(_, _) => "function".to_string().into(), + Self::Normal(f, _) => f.to_owned().into(), + Self::Custom(f, _) => format!("fn::{f}").into(), + } + } + /// Convert this function to an aggregate + pub fn aggregate(&self, val: Value) -> Self { + match self { + Self::Normal(n, a) => { + let mut a = a.to_owned(); + match a.len() { + 0 => a.insert(0, val), + _ => { + a.remove(0); + a.insert(0, val); + } + } + Self::Normal(n.to_owned(), a) + } + _ => unreachable!(), + } + } + /// Check if this function is a custom function + pub fn is_custom(&self) -> bool { + matches!(self, Self::Custom(_, _)) + } + + /// Check if this function is a scripting function + pub fn is_script(&self) -> bool { + matches!(self, Self::Script(_, _)) + } + + /// Check if this function has static arguments + pub fn is_static(&self) -> bool { + match self { + Self::Normal(_, a) => a.iter().all(Value::is_static), + _ => false, + } + } + + /// Check if this function is a rolling function + pub fn is_rolling(&self) -> bool { + match self { + Self::Normal(f, _) if f == "count" => true, + Self::Normal(f, _) if f == "math::max" => true, + Self::Normal(f, _) if f == "math::mean" => true, + Self::Normal(f, _) if f == "math::min" => true, + Self::Normal(f, _) if f == "math::sum" => true, + Self::Normal(f, _) if f == "time::max" => true, + Self::Normal(f, _) if f == "time::min" => true, + _ => false, + } + } + /// Check if this function is a grouping function + pub fn is_aggregate(&self) -> bool { + match self { + Self::Normal(f, _) if f == "array::distinct" => true, + Self::Normal(f, _) if f == "array::first" => true, + Self::Normal(f, _) if f == "array::flatten" => true, + Self::Normal(f, _) if f == "array::group" => true, + Self::Normal(f, _) if f == "array::last" => true, + Self::Normal(f, _) if f == "count" => true, + Self::Normal(f, _) if f == "math::bottom" => true, + Self::Normal(f, _) if f == "math::interquartile" => true, + Self::Normal(f, _) if f == "math::max" => true, + Self::Normal(f, _) if f == "math::mean" => true, + Self::Normal(f, _) if f == "math::median" => true, + Self::Normal(f, _) if f == "math::midhinge" => true, + Self::Normal(f, _) if f == "math::min" => true, + Self::Normal(f, _) if f == "math::mode" => true, + Self::Normal(f, _) if f == "math::nearestrank" => true, + Self::Normal(f, _) if f == "math::percentile" => true, + Self::Normal(f, _) if f == "math::sample" => true, + Self::Normal(f, _) if f == "math::spread" => true, + Self::Normal(f, _) if f == "math::stddev" => true, + Self::Normal(f, _) if f == "math::sum" => true, + Self::Normal(f, _) if f == "math::top" => true, + Self::Normal(f, _) if f == "math::trimean" => true, + Self::Normal(f, _) if f == "math::variance" => true, + Self::Normal(f, _) if f == "time::max" => true, + Self::Normal(f, _) if f == "time::min" => true, + _ => false, + } + } +} + +impl Function { + /// Process this type returning a computed simple Value + #[cfg_attr(not(target_arch = "wasm32"), async_recursion)] + #[cfg_attr(target_arch = "wasm32", async_recursion(?Send))] + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&'async_recursion CursorDoc<'_>>, + ) -> Result { + // Ensure futures are run + let opt = &opt.new_with_futures(true); + // Process the function type + match self { + Self::Normal(s, x) => { + // Check this function is allowed + ctx.check_allowed_function(s)?; + // Compute the function arguments + let a = try_join_all(x.iter().map(|v| v.compute(ctx, opt, txn, doc))).await?; + // Run the normal function + fnc::run(ctx, opt, txn, doc, s, a).await + } + Self::Custom(s, x) => { + // Check that a database is set to prevent a panic + opt.valid_for_db()?; + // Get the full name of this function + let name = format!("fn::{s}"); + // Check this function is allowed + ctx.check_allowed_function(name.as_str())?; + // Get the function definition + let val = { + // Claim transaction + let mut run = txn.lock().await; + // Get the function definition + run.get_and_cache_db_function(opt.ns(), opt.db(), s).await? + }; + // Check permissions + if opt.check_perms(Action::View) { + match &val.permissions { + Permission::Full => (), + Permission::None => { + return Err(Error::FunctionPermissions { + name: s.to_owned(), + }) + } + Permission::Specific(e) => { + // Disable permissions + let opt = &opt.new_with_perms(false); + // Process the PERMISSION clause + if !e.compute(ctx, opt, txn, doc).await?.is_truthy() { + return Err(Error::FunctionPermissions { + name: s.to_owned(), + }); + } + } + } + } + // Get the number of function arguments + let max_args_len = val.args.len(); + // Track the number of required arguments + let mut min_args_len = 0; + // Check for any final optional arguments + val.args.iter().rev().for_each(|(_, kind)| match kind { + Kind::Option(_) if min_args_len == 0 => {} + _ => min_args_len += 1, + }); + // Check the necessary arguments are passed + if x.len() < min_args_len || max_args_len < x.len() { + return Err(Error::InvalidArguments { + name: format!("fn::{}", val.name), + message: match (min_args_len, max_args_len) { + (1, 1) => String::from("The function expects 1 argument."), + (r, t) if r == t => format!("The function expects {r} arguments."), + (r, t) => format!("The function expects {r} to {t} arguments."), + }, + }); + } + // Compute the function arguments + let a = try_join_all(x.iter().map(|v| v.compute(ctx, opt, txn, doc))).await?; + // Duplicate context + let mut ctx = Context::new(ctx); + // Process the function arguments + for (val, (name, kind)) in a.into_iter().zip(&val.args) { + ctx.add_value(name.to_raw(), val.coerce_to(kind)?); + } + // Run the custom function + val.block.compute(&ctx, opt, txn, doc).await + } + #[allow(unused_variables)] + Self::Script(s, x) => { + #[cfg(feature = "scripting")] + { + // Check if scripting is allowed + ctx.check_allowed_scripting()?; + // Compute the function arguments + let a = try_join_all(x.iter().map(|v| v.compute(ctx, opt, txn, doc))).await?; + // Run the script function + fnc::script::run(ctx, opt, txn, doc, s, a).await + } + #[cfg(not(feature = "scripting"))] + { + Err(Error::InvalidScript { + message: String::from("Embedded functions are not enabled."), + }) + } + } + } + } +} + +impl fmt::Display for Function { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Normal(s, e) => write!(f, "{s}({})", Fmt::comma_separated(e)), + Self::Custom(s, e) => write!(f, "fn::{s}({})", Fmt::comma_separated(e)), + Self::Script(s, e) => write!(f, "function({}) {{{s}}}", Fmt::comma_separated(e)), + } + } +} diff --git a/core/src/sql/v2/future.rs b/core/src/sql/v2/future.rs new file mode 100644 index 00000000..564f5095 --- /dev/null +++ b/core/src/sql/v2/future.rs @@ -0,0 +1,46 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::block::Block; +use crate::sql::value::Value; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Future"; + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[serde(rename = "$surrealdb::private::sql::Future")] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Future(pub Block); + +impl From for Future { + fn from(v: Value) -> Self { + Future(Block::from(v)) + } +} + +impl Future { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + ) -> Result { + // Process the future if enabled + match opt.futures { + true => self.0.compute(ctx, opt, txn, doc).await?.ok(), + false => Ok(self.clone().into()), + } + } +} + +impl fmt::Display for Future { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, " {}", self.0) + } +} diff --git a/core/src/sql/v2/geometry.rs b/core/src/sql/v2/geometry.rs new file mode 100644 index 00000000..e36d8b14 --- /dev/null +++ b/core/src/sql/v2/geometry.rs @@ -0,0 +1,611 @@ +#![allow(clippy::derived_hash_with_manual_eq)] + +use crate::sql::array::Array; +use crate::sql::fmt::Fmt; +use crate::sql::value::Value; +use geo::algorithm::contains::Contains; +use geo::algorithm::intersects::Intersects; +use geo::{Coord, LineString, Point, Polygon}; +use geo_types::{MultiLineString, MultiPoint, MultiPolygon}; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::cmp::Ordering; +use std::iter::{once, FromIterator}; +use std::{fmt, hash}; + +pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Geometry"; + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename = "$surrealdb::private::sql::Geometry")] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub enum Geometry { + Point(Point), + Line(LineString), + Polygon(Polygon), + MultiPoint(MultiPoint), + MultiLine(MultiLineString), + MultiPolygon(MultiPolygon), + Collection(Vec), + // Add new variants here +} + +impl Geometry { + /// Check if this is a Point + pub fn is_point(&self) -> bool { + matches!(self, Self::Point(_)) + } + /// Check if this is a Line + pub fn is_line(&self) -> bool { + matches!(self, Self::Line(_)) + } + /// Check if this is a Polygon + pub fn is_polygon(&self) -> bool { + matches!(self, Self::Polygon(_)) + } + /// Check if this is a MultiPoint + pub fn is_multipoint(&self) -> bool { + matches!(self, Self::MultiPoint(_)) + } + /// Check if this is a MultiLine + pub fn is_multiline(&self) -> bool { + matches!(self, Self::MultiLine(_)) + } + /// Check if this is a MultiPolygon + pub fn is_multipolygon(&self) -> bool { + matches!(self, Self::MultiPolygon(_)) + } + /// Check if this is not a Collection + pub fn is_geometry(&self) -> bool { + !matches!(self, Self::Collection(_)) + } + /// Check if this is a Collection + pub fn is_collection(&self) -> bool { + matches!(self, Self::Collection(_)) + } + /// Get the type of this Geometry as text + pub fn as_type(&self) -> &'static str { + match self { + Self::Point(_) => "Point", + Self::Line(_) => "LineString", + Self::Polygon(_) => "Polygon", + Self::MultiPoint(_) => "MultiPoint", + Self::MultiLine(_) => "MultiLineString", + Self::MultiPolygon(_) => "MultiPolygon", + Self::Collection(_) => "GeometryCollection", + } + } + /// Get the raw coordinates of this Geometry as an Array + pub fn as_coordinates(&self) -> Value { + fn point(v: &Point) -> Value { + Array::from(vec![v.x(), v.y()]).into() + } + + fn line(v: &LineString) -> Value { + v.points().map(|v| point(&v)).collect::>().into() + } + + fn polygon(v: &Polygon) -> Value { + once(v.exterior()).chain(v.interiors()).map(line).collect::>().into() + } + + fn multipoint(v: &MultiPoint) -> Value { + v.iter().map(point).collect::>().into() + } + + fn multiline(v: &MultiLineString) -> Value { + v.iter().map(line).collect::>().into() + } + + fn multipolygon(v: &MultiPolygon) -> Value { + v.iter().map(polygon).collect::>().into() + } + + fn collection(v: &[Geometry]) -> Value { + v.iter().map(Geometry::as_coordinates).collect::>().into() + } + + match self { + Self::Point(v) => point(v), + Self::Line(v) => line(v), + Self::Polygon(v) => polygon(v), + Self::MultiPoint(v) => multipoint(v), + Self::MultiLine(v) => multiline(v), + Self::MultiPolygon(v) => multipolygon(v), + Self::Collection(v) => collection(v), + } + } +} + +impl PartialOrd for Geometry { + #[rustfmt::skip] + fn partial_cmp(&self, other: &Self) -> Option { + fn coord(v: &Coord) -> (f64, f64) { + v.x_y() + } + + fn point(v: &Point) -> (f64, f64) { + coord(&v.0) + } + + fn line(v: &LineString) -> impl Iterator + '_ { + v.into_iter().map(coord) + } + + fn polygon(v: &Polygon) -> impl Iterator + '_ { + v.interiors().iter().chain(once(v.exterior())).flat_map(line) + } + + fn multipoint(v: &MultiPoint) -> impl Iterator + '_ { + v.iter().map(point) + } + + fn multiline(v: &MultiLineString) -> impl Iterator + '_ { + v.iter().flat_map(line) + } + + fn multipolygon(v: &MultiPolygon) -> impl Iterator + '_ { + v.iter().flat_map(polygon) + } + + match (self, other) { + // + (Self::Point(_), Self::Line(_)) => Some(Ordering::Less), + (Self::Point(_), Self::Polygon(_)) => Some(Ordering::Less), + (Self::Point(_), Self::MultiPoint(_)) => Some(Ordering::Less), + (Self::Point(_), Self::MultiLine(_)) => Some(Ordering::Less), + (Self::Point(_), Self::MultiPolygon(_)) => Some(Ordering::Less), + (Self::Point(_), Self::Collection(_)) => Some(Ordering::Less), + // + (Self::Line(_), Self::Point(_)) => Some(Ordering::Greater), + (Self::Line(_), Self::Polygon(_)) => Some(Ordering::Less), + (Self::Line(_), Self::MultiPoint(_)) => Some(Ordering::Less), + (Self::Line(_), Self::MultiLine(_)) => Some(Ordering::Less), + (Self::Line(_), Self::MultiPolygon(_)) => Some(Ordering::Less), + (Self::Line(_), Self::Collection(_)) => Some(Ordering::Less), + // + (Self::Polygon(_), Self::Point(_)) => Some(Ordering::Greater), + (Self::Polygon(_), Self::Line(_)) => Some(Ordering::Greater), + (Self::Polygon(_), Self::MultiPoint(_)) => Some(Ordering::Less), + (Self::Polygon(_), Self::MultiLine(_)) => Some(Ordering::Less), + (Self::Polygon(_), Self::MultiPolygon(_)) => Some(Ordering::Less), + (Self::Polygon(_), Self::Collection(_)) => Some(Ordering::Less), + // + (Self::MultiPoint(_), Self::Point(_)) => Some(Ordering::Greater), + (Self::MultiPoint(_), Self::Line(_)) => Some(Ordering::Greater), + (Self::MultiPoint(_), Self::Polygon(_)) => Some(Ordering::Greater), + (Self::MultiPoint(_), Self::MultiLine(_)) => Some(Ordering::Less), + (Self::MultiPoint(_), Self::MultiPolygon(_)) => Some(Ordering::Less), + (Self::MultiPoint(_), Self::Collection(_)) => Some(Ordering::Less), + // + (Self::MultiLine(_), Self::Point(_)) => Some(Ordering::Greater), + (Self::MultiLine(_), Self::Line(_)) => Some(Ordering::Greater), + (Self::MultiLine(_), Self::Polygon(_)) => Some(Ordering::Greater), + (Self::MultiLine(_), Self::MultiPoint(_)) => Some(Ordering::Greater), + (Self::MultiLine(_), Self::MultiPolygon(_)) => Some(Ordering::Less), + (Self::MultiLine(_), Self::Collection(_)) => Some(Ordering::Less), + // + (Self::MultiPolygon(_), Self::Point(_)) => Some(Ordering::Greater), + (Self::MultiPolygon(_), Self::Line(_)) => Some(Ordering::Greater), + (Self::MultiPolygon(_), Self::Polygon(_)) => Some(Ordering::Greater), + (Self::MultiPolygon(_), Self::MultiPoint(_)) => Some(Ordering::Greater), + (Self::MultiPolygon(_), Self::MultiLine(_)) => Some(Ordering::Greater), + (Self::MultiPolygon(_), Self::Collection(_)) => Some(Ordering::Less), + // + (Self::Collection(_), Self::Point(_)) => Some(Ordering::Greater), + (Self::Collection(_), Self::Line(_)) => Some(Ordering::Greater), + (Self::Collection(_), Self::Polygon(_)) => Some(Ordering::Greater), + (Self::Collection(_), Self::MultiPoint(_)) => Some(Ordering::Greater), + (Self::Collection(_), Self::MultiLine(_)) => Some(Ordering::Greater), + (Self::Collection(_), Self::MultiPolygon(_)) => Some(Ordering::Greater), + // + (Self::Point(a), Self::Point(b)) => point(a).partial_cmp(&point(b)), + (Self::Line(a), Self::Line(b)) => line(a).partial_cmp(line(b)), + (Self::Polygon(a), Self::Polygon(b)) => polygon(a).partial_cmp(polygon(b)), + (Self::MultiPoint(a), Self::MultiPoint(b)) => multipoint(a).partial_cmp(multipoint(b)), + (Self::MultiLine(a), Self::MultiLine(b)) => multiline(a).partial_cmp(multiline(b)), + (Self::MultiPolygon(a), Self::MultiPolygon(b)) => multipolygon(a).partial_cmp(multipolygon(b)), + (Self::Collection(a), Self::Collection(b)) => a.partial_cmp(b), + } + } +} + +impl From<(f64, f64)> for Geometry { + fn from(v: (f64, f64)) -> Self { + Self::Point(v.into()) + } +} + +impl From<[f64; 2]> for Geometry { + fn from(v: [f64; 2]) -> Self { + Self::Point(v.into()) + } +} + +impl From> for Geometry { + fn from(v: Point) -> Self { + Self::Point(v) + } +} + +impl From> for Geometry { + fn from(v: LineString) -> Self { + Self::Line(v) + } +} + +impl From> for Geometry { + fn from(v: Polygon) -> Self { + Self::Polygon(v) + } +} + +impl From> for Geometry { + fn from(v: MultiPoint) -> Self { + Self::MultiPoint(v) + } +} + +impl From> for Geometry { + fn from(v: MultiLineString) -> Self { + Self::MultiLine(v) + } +} + +impl From> for Geometry { + fn from(v: MultiPolygon) -> Self { + Self::MultiPolygon(v) + } +} + +impl From> for Geometry { + fn from(v: Vec) -> Self { + Self::Collection(v) + } +} + +impl From>> for Geometry { + fn from(v: Vec>) -> Self { + Self::MultiPoint(MultiPoint(v)) + } +} + +impl From>> for Geometry { + fn from(v: Vec>) -> Self { + Self::MultiLine(MultiLineString(v)) + } +} + +impl From>> for Geometry { + fn from(v: Vec>) -> Self { + Self::MultiPolygon(MultiPolygon(v)) + } +} + +impl From for geo::Geometry { + fn from(v: Geometry) -> Self { + match v { + Geometry::Point(v) => v.into(), + Geometry::Line(v) => v.into(), + Geometry::Polygon(v) => v.into(), + Geometry::MultiPoint(v) => v.into(), + Geometry::MultiLine(v) => v.into(), + Geometry::MultiPolygon(v) => v.into(), + Geometry::Collection(v) => v.into_iter().collect::>(), + } + } +} + +impl FromIterator for geo::Geometry { + fn from_iter>(iter: I) -> Self { + let mut c: Vec> = vec![]; + for i in iter { + c.push(i.into()) + } + geo::Geometry::GeometryCollection(geo::GeometryCollection(c)) + } +} + +impl Geometry { + // ----------------------------------- + // Value operations + // ----------------------------------- + + pub fn contains(&self, other: &Self) -> bool { + match self { + Self::Point(v) => match other { + Self::Point(w) => v.contains(w), + Self::MultiPoint(w) => w.iter().all(|x| v.contains(x)), + Self::Collection(w) => w.iter().all(|x| self.contains(x)), + _ => false, + }, + Self::Line(v) => match other { + Self::Point(w) => v.contains(w), + Self::Line(w) => v.contains(w), + Self::MultiLine(w) => w.iter().all(|x| w.contains(x)), + Self::Collection(w) => w.iter().all(|x| self.contains(x)), + _ => false, + }, + Self::Polygon(v) => match other { + Self::Point(w) => v.contains(w), + Self::Line(w) => v.contains(w), + Self::Polygon(w) => v.contains(w), + Self::MultiPolygon(w) => w.iter().all(|x| w.contains(x)), + Self::Collection(w) => w.iter().all(|x| self.contains(x)), + _ => false, + }, + Self::MultiPoint(v) => match other { + Self::Point(w) => v.contains(w), + Self::MultiPoint(w) => w.iter().all(|x| w.contains(x)), + Self::Collection(w) => w.iter().all(|x| self.contains(x)), + _ => false, + }, + Self::MultiLine(v) => match other { + Self::Point(w) => v.contains(w), + Self::Line(w) => v.contains(w), + Self::MultiLine(w) => w.iter().all(|x| w.contains(x)), + Self::Collection(w) => w.iter().all(|x| self.contains(x)), + _ => false, + }, + Self::MultiPolygon(v) => match other { + Self::Point(w) => v.contains(w), + Self::Line(w) => v.contains(w), + Self::Polygon(w) => v.contains(w), + Self::MultiPoint(w) => v.contains(w), + Self::MultiLine(w) => v.contains(w), + Self::MultiPolygon(w) => v.contains(w), + Self::Collection(w) => w.iter().all(|x| self.contains(x)), + }, + Self::Collection(v) => v.iter().all(|x| x.contains(other)), + } + } + + pub fn intersects(&self, other: &Self) -> bool { + match self { + Self::Point(v) => match other { + Self::Point(w) => v.intersects(w), + Self::Line(w) => v.intersects(w), + Self::Polygon(w) => v.intersects(w), + Self::MultiPoint(w) => v.intersects(w), + Self::MultiLine(w) => w.iter().any(|x| v.intersects(x)), + Self::MultiPolygon(w) => v.intersects(w), + Self::Collection(w) => w.iter().all(|x| self.intersects(x)), + }, + Self::Line(v) => match other { + Self::Point(w) => v.intersects(w), + Self::Line(w) => v.intersects(w), + Self::Polygon(w) => v.intersects(w), + Self::MultiPoint(w) => v.intersects(w), + Self::MultiLine(w) => w.iter().any(|x| v.intersects(x)), + Self::MultiPolygon(w) => v.intersects(w), + Self::Collection(w) => w.iter().all(|x| self.intersects(x)), + }, + Self::Polygon(v) => match other { + Self::Point(w) => v.intersects(w), + Self::Line(w) => v.intersects(w), + Self::Polygon(w) => v.intersects(w), + Self::MultiPoint(w) => v.intersects(w), + Self::MultiLine(w) => v.intersects(w), + Self::MultiPolygon(w) => v.intersects(w), + Self::Collection(w) => w.iter().all(|x| self.intersects(x)), + }, + Self::MultiPoint(v) => match other { + Self::Point(w) => v.intersects(w), + Self::Line(w) => v.intersects(w), + Self::Polygon(w) => v.intersects(w), + Self::MultiPoint(w) => v.intersects(w), + Self::MultiLine(w) => w.iter().any(|x| v.intersects(x)), + Self::MultiPolygon(w) => v.intersects(w), + Self::Collection(w) => w.iter().all(|x| self.intersects(x)), + }, + Self::MultiLine(v) => match other { + Self::Point(w) => v.intersects(w), + Self::Line(w) => v.intersects(w), + Self::Polygon(w) => v.intersects(w), + Self::MultiPoint(w) => v.intersects(w), + Self::MultiLine(w) => w.iter().any(|x| v.intersects(x)), + Self::MultiPolygon(w) => v.intersects(w), + Self::Collection(w) => w.iter().all(|x| self.intersects(x)), + }, + Self::MultiPolygon(v) => match other { + Self::Point(w) => v.intersects(w), + Self::Line(w) => v.intersects(w), + Self::Polygon(w) => v.intersects(w), + Self::MultiPoint(w) => v.intersects(w), + Self::MultiLine(w) => v.intersects(w), + Self::MultiPolygon(w) => v.intersects(w), + Self::Collection(w) => w.iter().all(|x| self.intersects(x)), + }, + Self::Collection(v) => v.iter().all(|x| x.intersects(other)), + } + } +} + +impl fmt::Display for Geometry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Point(v) => { + write!(f, "({}, {})", v.x(), v.y()) + } + Self::Line(v) => write!( + f, + "{{ type: 'LineString', coordinates: [{}] }}", + Fmt::comma_separated(v.points().map(|v| Fmt::new(v, |v, f| write!( + f, + "[{}, {}]", + v.x(), + v.y() + )))) + ), + Self::Polygon(v) => write!( + f, + "{{ type: 'Polygon', coordinates: [[{}]{}] }}", + Fmt::comma_separated(v.exterior().points().map(|v| Fmt::new(v, |v, f| write!( + f, + "[{}, {}]", + v.x(), + v.y() + )))), + Fmt::new(v.interiors(), |interiors, f| { + match interiors.len() { + 0 => Ok(()), + _ => write!( + f, + ", [{}]", + Fmt::comma_separated(interiors.iter().map(|i| Fmt::new(i, |i, f| { + write!( + f, + "[{}]", + Fmt::comma_separated(i.points().map(|v| Fmt::new( + v, + |v, f| write!(f, "[{}, {}]", v.x(), v.y()) + ))) + ) + }))) + ), + } + }) + ), + Self::MultiPoint(v) => { + write!( + f, + "{{ type: 'MultiPoint', coordinates: [{}] }}", + Fmt::comma_separated(v.iter().map(|v| Fmt::new(v, |v, f| write!( + f, + "[{}, {}]", + v.x(), + v.y() + )))) + ) + } + Self::MultiLine(v) => write!( + f, + "{{ type: 'MultiLineString', coordinates: [{}] }}", + Fmt::comma_separated(v.iter().map(|v| Fmt::new(v, |v, f| write!( + f, + "[{}]", + Fmt::comma_separated(v.points().map(|v| Fmt::new(v, |v, f| write!( + f, + "[{}, {}]", + v.x(), + v.y() + )))) + )))) + ), + Self::MultiPolygon(v) => write!( + f, + "{{ type: 'MultiPolygon', coordinates: [{}] }}", + Fmt::comma_separated(v.iter().map(|v| Fmt::new(v, |v, f| { + write!( + f, + "[[{}]{}]", + Fmt::comma_separated( + v.exterior().points().map(|v| Fmt::new(v, |v, f| write!( + f, + "[{}, {}]", + v.x(), + v.y() + ))) + ), + Fmt::new(v.interiors(), |interiors, f| { + match interiors.len() { + 0 => Ok(()), + _ => write!( + f, + ", [{}]", + Fmt::comma_separated(interiors.iter().map(|i| Fmt::new( + i, + |i, f| { + write!( + f, + "[{}]", + Fmt::comma_separated(i.points().map(|v| Fmt::new( + v, + |v, f| write!(f, "[{}, {}]", v.x(), v.y()) + ))) + ) + } + ))) + ), + } + }) + ) + }))), + ), + Self::Collection(v) => { + write!( + f, + "{{ type: 'GeometryCollection', geometries: [{}] }}", + Fmt::comma_separated(v) + ) + } + } + } +} + +impl hash::Hash for Geometry { + fn hash(&self, state: &mut H) { + match self { + Geometry::Point(p) => { + "Point".hash(state); + p.x().to_bits().hash(state); + p.y().to_bits().hash(state); + } + Geometry::Line(l) => { + "Line".hash(state); + l.points().for_each(|v| { + v.x().to_bits().hash(state); + v.y().to_bits().hash(state); + }); + } + Geometry::Polygon(p) => { + "Polygon".hash(state); + p.exterior().points().for_each(|ext| { + ext.x().to_bits().hash(state); + ext.y().to_bits().hash(state); + }); + p.interiors().iter().for_each(|int| { + int.points().for_each(|v| { + v.x().to_bits().hash(state); + v.y().to_bits().hash(state); + }); + }); + } + Geometry::MultiPoint(v) => { + "MultiPoint".hash(state); + v.0.iter().for_each(|v| { + v.x().to_bits().hash(state); + v.y().to_bits().hash(state); + }); + } + Geometry::MultiLine(ml) => { + "MultiLine".hash(state); + ml.0.iter().for_each(|ls| { + ls.points().for_each(|p| { + p.x().to_bits().hash(state); + p.y().to_bits().hash(state); + }); + }); + } + Geometry::MultiPolygon(mp) => { + "MultiPolygon".hash(state); + mp.0.iter().for_each(|p| { + p.exterior().points().for_each(|ext| { + ext.x().to_bits().hash(state); + ext.y().to_bits().hash(state); + }); + p.interiors().iter().for_each(|int| { + int.points().for_each(|v| { + v.x().to_bits().hash(state); + v.y().to_bits().hash(state); + }); + }); + }); + } + Geometry::Collection(v) => { + "GeometryCollection".hash(state); + v.iter().for_each(|v| v.hash(state)); + } + } + } +} diff --git a/core/src/sql/v2/graph.rs b/core/src/sql/v2/graph.rs new file mode 100644 index 00000000..88590579 --- /dev/null +++ b/core/src/sql/v2/graph.rs @@ -0,0 +1,76 @@ +use crate::sql::cond::Cond; +use crate::sql::dir::Dir; +use crate::sql::field::Fields; +use crate::sql::group::Groups; +use crate::sql::idiom::Idiom; +use crate::sql::limit::Limit; +use crate::sql::order::Orders; +use crate::sql::split::Splits; +use crate::sql::start::Start; +use crate::sql::table::Tables; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Formatter, Write}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Graph { + pub dir: Dir, + pub expr: Fields, + pub what: Tables, + pub cond: Option, + pub split: Option, + pub group: Option, + pub order: Option, + pub limit: Option, + pub start: Option, + pub alias: Option, +} + +impl Graph { + /// Convert the graph edge to a raw String + pub fn to_raw(&self) -> String { + self.to_string() + } +} + +impl Display for Graph { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + if self.what.0.len() <= 1 && self.cond.is_none() && self.alias.is_none() { + Display::fmt(&self.dir, f)?; + match self.what.len() { + 0 => f.write_char('?'), + _ => Display::fmt(&self.what, f), + } + } else { + write!(f, "{}(", self.dir)?; + match self.what.len() { + 0 => f.write_char('?'), + _ => Display::fmt(&self.what, f), + }?; + if let Some(ref v) = self.cond { + write!(f, " {v}")? + } + if let Some(ref v) = self.split { + write!(f, " {v}")? + } + if let Some(ref v) = self.group { + write!(f, " {v}")? + } + if let Some(ref v) = self.order { + write!(f, " {v}")? + } + if let Some(ref v) = self.limit { + write!(f, " {v}")? + } + if let Some(ref v) = self.start { + write!(f, " {v}")? + } + if let Some(ref v) = self.alias { + write!(f, " AS {v}")? + } + f.write_char(')') + } + } +} diff --git a/core/src/sql/v2/group.rs b/core/src/sql/v2/group.rs new file mode 100644 index 00000000..4b92573b --- /dev/null +++ b/core/src/sql/v2/group.rs @@ -0,0 +1,54 @@ +use crate::sql::fmt::Fmt; +use crate::sql::idiom::Idiom; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Formatter}; +use std::ops::Deref; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Groups(pub Vec); + +impl Deref for Groups { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl IntoIterator for Groups { + type Item = Group; + type IntoIter = std::vec::IntoIter; + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl Display for Groups { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + if self.0.is_empty() { + write!(f, "GROUP ALL") + } else { + write!(f, "GROUP BY {}", Fmt::comma_separated(&self.0)) + } + } +} + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Group(pub Idiom); + +impl Deref for Group { + type Target = Idiom; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Display for Group { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + Display::fmt(&self.0, f) + } +} diff --git a/core/src/sql/v2/id.rs b/core/src/sql/v2/id.rs new file mode 100644 index 00000000..11412781 --- /dev/null +++ b/core/src/sql/v2/id.rs @@ -0,0 +1,206 @@ +use crate::cnf::ID_CHARS; +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::{escape::escape_rid, Array, Number, Object, Strand, Thing, Uuid, Value}; +use nanoid::nanoid; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; +use std::fmt::{self, Display, Formatter}; +use ulid::Ulid; + +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub enum Gen { + Rand, + Ulid, + Uuid, +} + +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub enum Id { + Number(i64), + String(String), + Array(Array), + Object(Object), + Generate(Gen), +} + +impl From for Id { + fn from(v: i64) -> Self { + Self::Number(v) + } +} + +impl From for Id { + fn from(v: i32) -> Self { + Self::Number(v as i64) + } +} + +impl From for Id { + fn from(v: u64) -> Self { + Self::Number(v as i64) + } +} + +impl From for Id { + fn from(v: String) -> Self { + Self::String(v) + } +} + +impl From for Id { + fn from(v: Array) -> Self { + Self::Array(v) + } +} + +impl From for Id { + fn from(v: Object) -> Self { + Self::Object(v) + } +} + +impl From for Id { + fn from(v: Uuid) -> Self { + Self::String(v.to_raw()) + } +} + +impl From for Id { + fn from(v: Strand) -> Self { + Self::String(v.as_string()) + } +} + +impl From<&str> for Id { + fn from(v: &str) -> Self { + Self::String(v.to_owned()) + } +} + +impl From<&String> for Id { + fn from(v: &String) -> Self { + Self::String(v.to_owned()) + } +} + +impl From> for Id { + fn from(v: Vec<&str>) -> Self { + Id::Array(v.into()) + } +} + +impl From> for Id { + fn from(v: Vec) -> Self { + Id::Array(v.into()) + } +} + +impl From> for Id { + fn from(v: Vec) -> Self { + Id::Array(v.into()) + } +} + +impl From> for Id { + fn from(v: BTreeMap) -> Self { + Id::Object(v.into()) + } +} + +impl From for Id { + fn from(v: Number) -> Self { + match v { + Number::Int(v) => v.into(), + Number::Float(v) => v.to_string().into(), + Number::Decimal(v) => v.to_string().into(), + } + } +} + +impl From for Id { + fn from(v: Thing) -> Self { + v.id + } +} + +impl Id { + /// Generate a new random ID + pub fn rand() -> Self { + Self::String(nanoid!(20, &ID_CHARS)) + } + /// Generate a new random ULID + pub fn ulid() -> Self { + Self::String(Ulid::new().to_string()) + } + /// Generate a new random UUID + pub fn uuid() -> Self { + Self::String(Uuid::new_v7().to_raw()) + } + /// Convert the Id to a raw String + pub fn to_raw(&self) -> String { + match self { + Self::Number(v) => v.to_string(), + Self::String(v) => v.to_string(), + Self::Array(v) => v.to_string(), + Self::Object(v) => v.to_string(), + Self::Generate(v) => match v { + Gen::Rand => "rand()".to_string(), + Gen::Ulid => "ulid()".to_string(), + Gen::Uuid => "uuid()".to_string(), + }, + } + } +} + +impl Display for Id { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + Self::Number(v) => Display::fmt(v, f), + Self::String(v) => Display::fmt(&escape_rid(v), f), + Self::Array(v) => Display::fmt(v, f), + Self::Object(v) => Display::fmt(v, f), + Self::Generate(v) => match v { + Gen::Rand => Display::fmt("rand()", f), + Gen::Ulid => Display::fmt("ulid()", f), + Gen::Uuid => Display::fmt("uuid()", f), + }, + } + } +} + +impl Id { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + ) -> Result { + match self { + Id::Number(v) => Ok(Id::Number(*v)), + Id::String(v) => Ok(Id::String(v.clone())), + Id::Array(v) => match v.compute(ctx, opt, txn, doc).await? { + Value::Array(v) => Ok(Id::Array(v)), + _ => unreachable!(), + }, + Id::Object(v) => match v.compute(ctx, opt, txn, doc).await? { + Value::Object(v) => Ok(Id::Object(v)), + _ => unreachable!(), + }, + Id::Generate(v) => match v { + Gen::Rand => Ok(Self::rand()), + Gen::Ulid => Ok(Self::ulid()), + Gen::Uuid => Ok(Self::uuid()), + }, + } + } +} diff --git a/core/src/sql/v2/ident.rs b/core/src/sql/v2/ident.rs new file mode 100644 index 00000000..20366292 --- /dev/null +++ b/core/src/sql/v2/ident.rs @@ -0,0 +1,59 @@ +use crate::sql::{escape::escape_ident, strand::no_nul_bytes}; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Formatter}; +use std::ops::Deref; +use std::str; + +#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Ident(#[serde(with = "no_nul_bytes")] pub String); + +impl From for Ident { + fn from(v: String) -> Self { + Self(v) + } +} + +impl From<&str> for Ident { + fn from(v: &str) -> Self { + Self::from(String::from(v)) + } +} + +impl Deref for Ident { + type Target = String; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Ident { + /// Convert the Ident to a raw String + pub fn to_raw(&self) -> String { + self.0.to_string() + } + /// Checks if this field is the `id` field + pub(crate) fn is_id(&self) -> bool { + self.0.as_str() == "id" + } + /// Checks if this field is the `type` field + pub(crate) fn is_type(&self) -> bool { + self.0.as_str() == "type" + } + /// Checks if this field is the `coordinates` field + pub(crate) fn is_coordinates(&self) -> bool { + self.0.as_str() == "coordinates" + } + /// Checks if this field is the `geometries` field + pub(crate) fn is_geometries(&self) -> bool { + self.0.as_str() == "geometries" + } +} + +impl Display for Ident { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + Display::fmt(&escape_ident(&self.0), f) + } +} diff --git a/core/src/sql/v2/idiom.rs b/core/src/sql/v2/idiom.rs new file mode 100644 index 00000000..aa40210c --- /dev/null +++ b/core/src/sql/v2/idiom.rs @@ -0,0 +1,192 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::{ + fmt::{fmt_separated_by, Fmt}, + part::Next, + paths::{ID, IN, META, OUT}, + Part, Value, +}; +use md5::{Digest, Md5}; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Formatter}; +use std::ops::Deref; +use std::str; + +pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Idiom"; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Idioms(pub Vec); + +impl Deref for Idioms { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl IntoIterator for Idioms { + type Item = Idiom; + type IntoIter = std::vec::IntoIter; + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl Display for Idioms { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + Display::fmt(&Fmt::comma_separated(&self.0), f) + } +} + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[serde(rename = "$surrealdb::private::sql::Idiom")] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Idiom(pub Vec); + +impl Deref for Idiom { + type Target = [Part]; + fn deref(&self) -> &Self::Target { + self.0.as_slice() + } +} + +impl From for Idiom { + fn from(v: String) -> Self { + Self(vec![Part::from(v)]) + } +} + +impl From> for Idiom { + fn from(v: Vec) -> Self { + Self(v) + } +} + +impl From<&[Part]> for Idiom { + fn from(v: &[Part]) -> Self { + Self(v.to_vec()) + } +} +impl From for Idiom { + fn from(v: Part) -> Self { + Self(vec![v]) + } +} + +impl Idiom { + /// Appends a part to the end of this Idiom + pub(crate) fn push(mut self, n: Part) -> Idiom { + self.0.push(n); + self + } + /// Convert this Idiom to a unique hash + pub(crate) fn to_hash(&self) -> String { + let mut hasher = Md5::new(); + hasher.update(self.to_string().as_str()); + format!("{:x}", hasher.finalize()) + } + /// Convert this Idiom to a JSON Path string + pub(crate) fn to_path(&self) -> String { + format!("/{self}").replace(']', "").replace(&['.', '['][..], "/") + } + /// Simplifies this Idiom for use in object keys + pub(crate) fn simplify(&self) -> Idiom { + self.0 + .iter() + .filter(|&p| { + matches!(p, Part::Field(_) | Part::Start(_) | Part::Value(_) | Part::Graph(_)) + }) + .cloned() + .collect::>() + .into() + } + /// Check if this Idiom is an 'id' field + pub(crate) fn is_id(&self) -> bool { + self.0.len() == 1 && self.0[0].eq(&ID[0]) + } + /// Check if this Idiom is an 'in' field + pub(crate) fn is_in(&self) -> bool { + self.0.len() == 1 && self.0[0].eq(&IN[0]) + } + /// Check if this Idiom is an 'out' field + pub(crate) fn is_out(&self) -> bool { + self.0.len() == 1 && self.0[0].eq(&OUT[0]) + } + /// Check if this Idiom is a 'meta' field + pub(crate) fn is_meta(&self) -> bool { + self.0.len() == 1 && self.0[0].eq(&META[0]) + } + /// Check if this is an expression with multiple yields + pub(crate) fn is_multi_yield(&self) -> bool { + self.iter().any(Self::split_multi_yield) + } + /// Check if the path part is a yield in a multi-yield expression + pub(crate) fn split_multi_yield(v: &Part) -> bool { + matches!(v, Part::Graph(g) if g.alias.is_some()) + } + /// Check if the path part is a yield in a multi-yield expression + pub(crate) fn remove_trailing_all(&mut self) { + if self.ends_with(&[Part::All]) { + self.0.truncate(self.len() - 1); + } + } +} + +impl Idiom { + /// Check if we require a writeable transaction + pub(crate) fn writeable(&self) -> bool { + self.0.iter().any(|v| v.writeable()) + } + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + ) -> Result { + match self.first() { + // The starting part is a value + Some(Part::Start(v)) => { + v.compute(ctx, opt, txn, doc) + .await? + .get(ctx, opt, txn, doc, self.as_ref().next()) + .await? + .compute(ctx, opt, txn, doc) + .await + } + // Otherwise use the current document + _ => match doc { + // There is a current document + Some(v) => { + v.doc.get(ctx, opt, txn, doc, self).await?.compute(ctx, opt, txn, doc).await + } + // There isn't any document + None => Ok(Value::None), + }, + } + } +} + +impl Display for Idiom { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt( + &Fmt::new( + self.0.iter().enumerate().map(|args| { + Fmt::new(args, |(i, p), f| match (i, p) { + (0, Part::Field(v)) => Display::fmt(v, f), + _ => Display::fmt(p, f), + }) + }), + fmt_separated_by(""), + ), + f, + ) + } +} diff --git a/core/src/sql/v2/index.rs b/core/src/sql/v2/index.rs new file mode 100644 index 00000000..077c34ca --- /dev/null +++ b/core/src/sql/v2/index.rs @@ -0,0 +1,168 @@ +use crate::err::Error; +use crate::fnc::util::math::vector::{ + ChebyshevDistance, CosineSimilarity, EuclideanDistance, HammingDistance, JaccardSimilarity, + ManhattanDistance, MinkowskiDistance, PearsonSimilarity, +}; +use crate::sql::ident::Ident; +use crate::sql::scoring::Scoring; +use crate::sql::Number; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; +use std::fmt::{Display, Formatter}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub enum Index { + /// (Basic) non unique + #[default] + Idx, + /// Unique index + Uniq, + /// Index with Full-Text search capabilities + Search(SearchParams), + /// M-Tree index for distance based metrics + MTree(MTreeParams), +} + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 2)] +pub struct SearchParams { + pub az: Ident, + pub hl: bool, + pub sc: Scoring, + pub doc_ids_order: u32, + pub doc_lengths_order: u32, + pub postings_order: u32, + pub terms_order: u32, + #[revision(start = 2)] + pub doc_ids_cache: u32, + #[revision(start = 2)] + pub doc_lengths_cache: u32, + #[revision(start = 2)] + pub postings_cache: u32, + #[revision(start = 2)] + pub terms_cache: u32, +} + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 2)] +pub struct MTreeParams { + pub dimension: u16, + pub distance: Distance, + pub vector_type: VectorType, + pub capacity: u16, + pub doc_ids_order: u32, + #[revision(start = 2)] + pub doc_ids_cache: u32, + #[revision(start = 2)] + pub mtree_cache: u32, +} + +#[derive(Clone, Default, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub enum Distance { + Chebyshev, + Cosine, + #[default] + Euclidean, + Hamming, + Jaccard, + Manhattan, + Minkowski(Number), + Pearson, +} + +impl Distance { + pub(crate) fn compute(&self, v1: &Vec, v2: &Vec) -> Result { + match self { + Distance::Cosine => v1.cosine_similarity(v2), + Distance::Chebyshev => v1.chebyshev_distance(v2), + Distance::Euclidean => v1.euclidean_distance(v2), + Distance::Hamming => v1.hamming_distance(v2), + Distance::Jaccard => v1.jaccard_similarity(v2), + Distance::Manhattan => v1.manhattan_distance(v2), + Distance::Minkowski(r) => v1.minkowski_distance(v2, r), + Distance::Pearson => v1.pearson_similarity(v2), + } + } +} + +impl Display for Distance { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + Self::Chebyshev => f.write_str("CHEBYSHEV"), + Self::Cosine => f.write_str("COSINE"), + Self::Euclidean => f.write_str("EUCLIDEAN"), + Self::Hamming => f.write_str("HAMMING"), + Self::Jaccard => f.write_str("JACCARD"), + Self::Manhattan => f.write_str("MANHATTAN"), + Self::Minkowski(order) => write!(f, "MINKOWSKI {}", order), + Self::Pearson => f.write_str("PEARSON"), + } + } +} + +#[derive(Clone, Copy, Default, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub enum VectorType { + #[default] + F64, + F32, + I64, + I32, + I16, +} + +impl Display for VectorType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + Self::F64 => f.write_str("F64"), + Self::F32 => f.write_str("F32"), + Self::I64 => f.write_str("I64"), + Self::I32 => f.write_str("I32"), + Self::I16 => f.write_str("I16"), + } + } +} + +impl Display for Index { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + Self::Idx => Ok(()), + Self::Uniq => f.write_str("UNIQUE"), + Self::Search(p) => { + write!( + f, + "SEARCH ANALYZER {} {} DOC_IDS_ORDER {} DOC_LENGTHS_ORDER {} POSTINGS_ORDER {} TERMS_ORDER {} DOC_IDS_CACHE {} DOC_LENGTHS_CACHE {} POSTINGS_CACHE {} TERMS_CACHE {}", + p.az, + p.sc, + p.doc_ids_order, + p.doc_lengths_order, + p.postings_order, + p.terms_order, + p.doc_ids_cache, + p.doc_lengths_cache, + p.postings_cache, + p.terms_cache + )?; + if p.hl { + f.write_str(" HIGHLIGHTS")? + } + Ok(()) + } + Self::MTree(p) => { + write!( + f, + "MTREE DIMENSION {} DIST {} TYPE {} CAPACITY {} DOC_IDS_ORDER {} DOC_IDS_CACHE {} MTREE_CACHE {}", + p.dimension, p.distance, p.vector_type, p.capacity, p.doc_ids_order, p.doc_ids_cache, p.mtree_cache + ) + } + } + } +} diff --git a/core/src/sql/v2/kind.rs b/core/src/sql/v2/kind.rs new file mode 100644 index 00000000..1de38bbc --- /dev/null +++ b/core/src/sql/v2/kind.rs @@ -0,0 +1,131 @@ +use crate::sql::{fmt::Fmt, Table}; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Formatter}; + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub enum Kind { + Any, + Null, + Bool, + Bytes, + Datetime, + Decimal, + Duration, + Float, + Int, + Number, + Object, + Point, + String, + Uuid, + Record(Vec), + Geometry(Vec), + Option(Box), + Either(Vec), + Set(Box, Option), + Array(Box, Option), +} + +impl Default for Kind { + fn default() -> Self { + Self::Any + } +} + +impl Kind { + fn is_any(&self) -> bool { + matches!(self, Kind::Any) + } + + // return the kind of the contained value. + // + // For example: for `array` or `set` this returns `number`. + // For `array | set` this returns `number | float`. + pub(crate) fn inner_kind(&self) -> Option { + let mut this = self; + loop { + match &this { + Kind::Any + | Kind::Null + | Kind::Bool + | Kind::Bytes + | Kind::Datetime + | Kind::Decimal + | Kind::Duration + | Kind::Float + | Kind::Int + | Kind::Number + | Kind::Object + | Kind::Point + | Kind::String + | Kind::Uuid + | Kind::Record(_) + | Kind::Geometry(_) => return None, + Kind::Option(x) => { + this = x; + } + Kind::Array(x, _) | Kind::Set(x, _) => return Some(x.as_ref().clone()), + Kind::Either(x) => { + // a either shouldn't be able to contain a either itself so recursing here + // should be fine. + let kinds: Vec = x.iter().filter_map(Self::inner_kind).collect(); + if kinds.is_empty() { + return None; + } + return Some(Kind::Either(kinds)); + } + } + } + } +} + +impl From<&Kind> for Box { + #[inline] + fn from(v: &Kind) -> Self { + Box::new(v.clone()) + } +} + +impl Display for Kind { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + Kind::Any => f.write_str("any"), + Kind::Null => f.write_str("null"), + Kind::Bool => f.write_str("bool"), + Kind::Bytes => f.write_str("bytes"), + Kind::Datetime => f.write_str("datetime"), + Kind::Decimal => f.write_str("decimal"), + Kind::Duration => f.write_str("duration"), + Kind::Float => f.write_str("float"), + Kind::Int => f.write_str("int"), + Kind::Number => f.write_str("number"), + Kind::Object => f.write_str("object"), + Kind::Point => f.write_str("point"), + Kind::String => f.write_str("string"), + Kind::Uuid => f.write_str("uuid"), + Kind::Option(k) => write!(f, "option<{}>", k), + Kind::Record(k) => match k { + k if k.is_empty() => write!(f, "record"), + k => write!(f, "record<{}>", Fmt::verbar_separated(k)), + }, + Kind::Geometry(k) => match k { + k if k.is_empty() => write!(f, "geometry"), + k => write!(f, "geometry<{}>", Fmt::verbar_separated(k)), + }, + Kind::Set(k, l) => match (k, l) { + (k, None) if k.is_any() => write!(f, "set"), + (k, None) => write!(f, "set<{k}>"), + (k, Some(l)) => write!(f, "set<{k}, {l}>"), + }, + Kind::Array(k, l) => match (k, l) { + (k, None) if k.is_any() => write!(f, "array"), + (k, None) => write!(f, "array<{k}>"), + (k, Some(l)) => write!(f, "array<{k}, {l}>"), + }, + Kind::Either(k) => write!(f, "{}", Fmt::verbar_separated(k)), + } + } +} diff --git a/core/src/sql/v2/language.rs b/core/src/sql/v2/language.rs new file mode 100644 index 00000000..552d931f --- /dev/null +++ b/core/src/sql/v2/language.rs @@ -0,0 +1,57 @@ +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; +use std::fmt::Display; + +#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub enum Language { + Arabic, + Danish, + Dutch, + English, + French, + German, + Greek, + Hungarian, + Italian, + Norwegian, + Portuguese, + Romanian, + Russian, + Spanish, + Swedish, + Tamil, + Turkish, +} + +impl Language { + pub fn as_str(&self) -> &'static str { + match self { + Self::Arabic => "ARABIC", + Self::Danish => "DANISH", + Self::Dutch => "DUTCH", + Self::English => "ENGLISH", + Self::French => "FRENCH", + Self::German => "GERMAN", + Self::Greek => "GREEK", + Self::Hungarian => "HUNGARIAN", + Self::Italian => "ITALIAN", + Self::Norwegian => "NORWEGIAN", + Self::Portuguese => "PORTUGUESE", + Self::Romanian => "ROMANIAN", + Self::Russian => "RUSSIAN", + Self::Spanish => "SPANISH", + Self::Swedish => "SWEDISH", + Self::Tamil => "TAMIL", + Self::Turkish => "TURKISH", + } + } +} + +impl Display for Language { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(self.as_str()) + } +} diff --git a/core/src/sql/v2/limit.rs b/core/src/sql/v2/limit.rs new file mode 100644 index 00000000..46f3e0c4 --- /dev/null +++ b/core/src/sql/v2/limit.rs @@ -0,0 +1,41 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::number::Number; +use crate::sql::value::Value; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Limit(pub Value); + +impl Limit { + pub(crate) async fn process( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + ) -> Result { + match self.0.compute(ctx, opt, txn, doc).await { + // This is a valid limiting number + Ok(Value::Number(Number::Int(v))) if v >= 0 => Ok(v as usize), + // An invalid value was specified + Ok(v) => Err(Error::InvalidLimit { + value: v.as_string(), + }), + // A different error occurred + Err(e) => Err(e), + } + } +} + +impl fmt::Display for Limit { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "LIMIT {}", self.0) + } +} diff --git a/core/src/sql/v2/mock.rs b/core/src/sql/v2/mock.rs new file mode 100644 index 00000000..8e878082 --- /dev/null +++ b/core/src/sql/v2/mock.rs @@ -0,0 +1,78 @@ +use crate::sql::{escape::escape_ident, Id, Thing}; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Mock"; + +pub struct IntoIter { + model: Mock, + index: u64, +} + +impl Iterator for IntoIter { + type Item = Thing; + fn next(&mut self) -> Option { + match &self.model { + Mock::Count(tb, c) => { + if self.index < *c { + self.index += 1; + Some(Thing { + tb: tb.to_string(), + id: Id::rand(), + }) + } else { + None + } + } + Mock::Range(tb, b, e) => { + if self.index == 0 { + self.index = *b - 1; + } + if self.index < *e { + self.index += 1; + Some(Thing { + tb: tb.to_string(), + id: Id::from(self.index), + }) + } else { + None + } + } + } + } +} + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[serde(rename = "$surrealdb::private::sql::Mock")] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub enum Mock { + Count(String, u64), + Range(String, u64, u64), + // Add new variants here +} + +impl IntoIterator for Mock { + type Item = Thing; + type IntoIter = IntoIter; + fn into_iter(self) -> Self::IntoIter { + IntoIter { + model: self, + index: 0, + } + } +} + +impl fmt::Display for Mock { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Mock::Count(tb, c) => { + write!(f, "|{}:{}|", escape_ident(tb), c) + } + Mock::Range(tb, b, e) => { + write!(f, "|{}:{}..{}|", escape_ident(tb), b, e) + } + } + } +} diff --git a/core/src/sql/v2/mod.rs b/core/src/sql/v2/mod.rs new file mode 100644 index 00000000..9afc9d36 --- /dev/null +++ b/core/src/sql/v2/mod.rs @@ -0,0 +1,154 @@ +//! The full type definitions for the SurrealQL query language + +pub(crate) mod algorithm; +#[cfg(feature = "arbitrary")] +pub(crate) mod arbitrary; +pub(crate) mod array; +pub(crate) mod base; +pub(crate) mod block; +pub(crate) mod bytes; +pub(crate) mod cast; +pub(crate) mod changefeed; +pub(crate) mod cond; +pub(crate) mod constant; +pub(crate) mod data; +pub(crate) mod datetime; +pub(crate) mod dir; +pub(crate) mod duration; +pub(crate) mod edges; +pub(crate) mod escape; +pub(crate) mod explain; +pub(crate) mod expression; +pub(crate) mod fetch; +pub(crate) mod field; +pub(crate) mod filter; +pub(crate) mod fmt; +pub(crate) mod function; +pub(crate) mod future; +pub(crate) mod geometry; +pub(crate) mod graph; +pub(crate) mod group; +pub(crate) mod id; +pub(crate) mod ident; +pub(crate) mod idiom; +pub(crate) mod kind; +pub(crate) mod language; +pub(crate) mod limit; +pub(crate) mod mock; +pub(crate) mod model; +pub(crate) mod number; +pub(crate) mod object; +pub(crate) mod operation; +pub(crate) mod operator; +pub(crate) mod order; +pub(crate) mod output; +pub(crate) mod param; +pub(crate) mod part; +pub(crate) mod paths; +pub(crate) mod permission; +pub(crate) mod query; +pub(crate) mod range; +pub(crate) mod regex; +pub(crate) mod scoring; +pub(crate) mod script; +pub(crate) mod split; +pub(crate) mod start; +pub(crate) mod statement; +pub(crate) mod strand; +pub(crate) mod subquery; +pub(crate) mod table; +pub(crate) mod thing; +pub(crate) mod timeout; +pub(crate) mod tokenizer; +pub(crate) mod uuid; +pub(crate) mod value; +pub(crate) mod version; +pub(crate) mod view; +pub(crate) mod with; + +#[doc(hidden)] +pub mod index; + +pub mod serde; +pub mod statements; + +pub use self::algorithm::Algorithm; +pub use self::array::Array; +pub use self::base::Base; +pub use self::block::Block; +pub use self::bytes::Bytes; +pub use self::cast::Cast; +pub use self::changefeed::ChangeFeed; +pub use self::cond::Cond; +pub use self::constant::Constant; +pub use self::data::Data; +pub use self::datetime::Datetime; +pub use self::dir::Dir; +pub use self::duration::Duration; +pub use self::edges::Edges; +pub use self::explain::Explain; +pub use self::expression::Expression; +pub use self::fetch::Fetch; +pub use self::fetch::Fetchs; +pub use self::field::Field; +pub use self::field::Fields; +pub use self::function::Function; +pub use self::future::Future; +pub use self::geometry::Geometry; +pub use self::graph::Graph; +pub use self::group::Group; +pub use self::group::Groups; +pub use self::id::Id; +pub use self::ident::Ident; +pub use self::idiom::Idiom; +pub use self::idiom::Idioms; +pub use self::index::Index; +pub use self::kind::Kind; +pub use self::limit::Limit; +pub use self::mock::Mock; +pub use self::model::Model; +pub use self::number::Number; +pub use self::object::Object; +pub use self::operation::Operation; +pub use self::operator::Operator; +pub use self::order::Order; +pub use self::order::Orders; +pub use self::output::Output; +pub use self::param::Param; +pub use self::part::Part; +pub use self::permission::Permission; +pub use self::permission::Permissions; +pub use self::query::Query; +pub use self::range::Range; +pub use self::regex::Regex; +pub use self::scoring::Scoring; +pub use self::script::Script; +pub use self::split::Split; +pub use self::split::Splits; +pub use self::start::Start; +pub use self::statement::Statement; +pub use self::statement::Statements; +pub use self::strand::Strand; +pub use self::subquery::Subquery; +pub use self::table::Table; +pub use self::table::Tables; +pub use self::thing::Thing; +pub use self::timeout::Timeout; +pub use self::tokenizer::Tokenizer; +pub use self::uuid::Uuid; +pub use self::value::serde::to_value; +#[doc(hidden)] +pub use self::value::serde::{from_value, FromValueError}; +pub use self::value::Value; +pub use self::value::Values; +pub use self::version::Version; +pub use self::view::View; +pub use self::with::With; + +// module reexporting parsing function to prevent a breaking change. +#[doc(hidden)] +mod parser { + pub use crate::syn::*; +} + +pub use self::parser::{idiom, json, parse, subquery, thing, value}; diff --git a/core/src/sql/v2/model.rs b/core/src/sql/v2/model.rs new file mode 100644 index 00000000..205627a1 --- /dev/null +++ b/core/src/sql/v2/model.rs @@ -0,0 +1,211 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::value::Value; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[cfg(feature = "ml")] +use crate::iam::Action; +#[cfg(feature = "ml")] +use crate::sql::Permission; +#[cfg(feature = "ml")] +use futures::future::try_join_all; +#[cfg(feature = "ml")] +use std::collections::HashMap; +#[cfg(feature = "ml")] +use surrealml_core::execution::compute::ModelComputation; +#[cfg(feature = "ml")] +use surrealml_core::storage::surml_file::SurMlFile; + +#[cfg(feature = "ml")] +const ARGUMENTS: &str = "The model expects 1 argument. The argument can be either a number, an object, or an array of numbers."; + +#[derive(Clone, Debug, Default, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct Model { + pub name: String, + pub version: String, + pub args: Vec, +} + +impl fmt::Display for Model { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "ml::{}<{}>(", self.name, self.version)?; + for (idx, p) in self.args.iter().enumerate() { + if idx != 0 { + write!(f, ",")?; + } + write!(f, "{}", p)?; + } + write!(f, ")") + } +} + +impl Model { + #[cfg(feature = "ml")] + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + ) -> Result { + // Ensure futures are run + let opt = &opt.new_with_futures(true); + // Get the full name of this model + let name = format!("ml::{}", self.name); + // Check this function is allowed + ctx.check_allowed_function(name.as_str())?; + // Get the model definition + let val = { + // Claim transaction + let mut run = txn.lock().await; + // Get the function definition + run.get_and_cache_db_model(opt.ns(), opt.db(), &self.name, &self.version).await? + }; + // Calculate the model path + let path = format!( + "ml/{}/{}/{}-{}-{}.surml", + opt.ns(), + opt.db(), + self.name, + self.version, + val.hash + ); + // Check permissions + if opt.check_perms(Action::View) { + match &val.permissions { + Permission::Full => (), + Permission::None => { + return Err(Error::FunctionPermissions { + name: self.name.to_owned(), + }) + } + Permission::Specific(e) => { + // Disable permissions + let opt = &opt.new_with_perms(false); + // Process the PERMISSION clause + if !e.compute(ctx, opt, txn, doc).await?.is_truthy() { + return Err(Error::FunctionPermissions { + name: self.name.to_owned(), + }); + } + } + } + } + // Compute the function arguments + let mut args = + try_join_all(self.args.iter().map(|v| v.compute(ctx, opt, txn, doc))).await?; + // Check the minimum argument length + if args.len() != 1 { + return Err(Error::InvalidArguments { + name: format!("ml::{}<{}>", self.name, self.version), + message: ARGUMENTS.into(), + }); + } + // Take the first and only specified argument + match args.swap_remove(0) { + // Perform bufferered compute + Value::Object(v) => { + // Compute the model function arguments + let mut args = v + .into_iter() + .map(|(k, v)| Ok((k, Value::try_into(v)?))) + .collect::, Error>>() + .map_err(|_| Error::InvalidArguments { + name: format!("ml::{}<{}>", self.name, self.version), + message: ARGUMENTS.into(), + })?; + // Get the model file as bytes + let bytes = crate::obs::get(&path).await?; + // Run the compute in a blocking task + let outcome = tokio::task::spawn_blocking(move || { + let mut file = SurMlFile::from_bytes(bytes).unwrap(); + let compute_unit = ModelComputation { + surml_file: &mut file, + }; + compute_unit.buffered_compute(&mut args).map_err(Error::ModelComputation) + }) + .await + .unwrap()?; + // Convert the output to a value + Ok(outcome[0].into()) + } + // Perform raw compute + Value::Number(v) => { + // Compute the model function arguments + let args: f32 = v.try_into().map_err(|_| Error::InvalidArguments { + name: format!("ml::{}<{}>", self.name, self.version), + message: ARGUMENTS.into(), + })?; + // Get the model file as bytes + let bytes = crate::obs::get(&path).await?; + // Convert the argument to a tensor + let tensor = ndarray::arr1::(&[args]).into_dyn(); + // Run the compute in a blocking task + let outcome = tokio::task::spawn_blocking(move || { + let mut file = SurMlFile::from_bytes(bytes).unwrap(); + let compute_unit = ModelComputation { + surml_file: &mut file, + }; + compute_unit.raw_compute(tensor, None).map_err(Error::ModelComputation) + }) + .await + .unwrap()?; + // Convert the output to a value + Ok(outcome[0].into()) + } + // Perform raw compute + Value::Array(v) => { + // Compute the model function arguments + let args = v + .into_iter() + .map(Value::try_into) + .collect::, Error>>() + .map_err(|_| Error::InvalidArguments { + name: format!("ml::{}<{}>", self.name, self.version), + message: ARGUMENTS.into(), + })?; + // Get the model file as bytes + let bytes = crate::obs::get(&path).await?; + // Convert the argument to a tensor + let tensor = ndarray::arr1::(&args).into_dyn(); + // Run the compute in a blocking task + let outcome = tokio::task::spawn_blocking(move || { + let mut file = SurMlFile::from_bytes(bytes).unwrap(); + let compute_unit = ModelComputation { + surml_file: &mut file, + }; + compute_unit.raw_compute(tensor, None).map_err(Error::ModelComputation) + }) + .await + .unwrap()?; + // Convert the output to a value + Ok(outcome[0].into()) + } + // + _ => Err(Error::InvalidArguments { + name: format!("ml::{}<{}>", self.name, self.version), + message: ARGUMENTS.into(), + }), + } + } + + #[cfg(not(feature = "ml"))] + pub(crate) async fn compute( + &self, + _ctx: &Context<'_>, + _opt: &Options, + _txn: &Transaction, + _doc: Option<&CursorDoc<'_>>, + ) -> Result { + Err(Error::InvalidModel { + message: String::from("Machine learning computation is not enabled."), + }) + } +} diff --git a/core/src/sql/v2/number.rs b/core/src/sql/v2/number.rs new file mode 100644 index 00000000..f09fc0a9 --- /dev/null +++ b/core/src/sql/v2/number.rs @@ -0,0 +1,739 @@ +use super::value::{TryAdd, TryDiv, TryMul, TryNeg, TryPow, TryRem, TrySub}; +use crate::err::Error; +use crate::sql::strand::Strand; +use revision::revisioned; +use rust_decimal::prelude::*; +use serde::{Deserialize, Serialize}; +use std::cmp::Ordering; +use std::fmt::{self, Display, Formatter}; +use std::hash; +use std::iter::Product; +use std::iter::Sum; +use std::ops::{self, Add, Div, Mul, Neg, Rem, Sub}; +use std::str::FromStr; + +pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Number"; + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename = "$surrealdb::private::sql::Number")] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub enum Number { + Int(i64), + Float(f64), + Decimal(Decimal), + // Add new variants here +} + +impl Default for Number { + fn default() -> Self { + Self::Int(0) + } +} + +macro_rules! from_prim_ints { + ($($int: ty),*) => { + $( + impl From<$int> for Number { + fn from(i: $int) -> Self { + Self::Int(i as i64) + } + } + )* + }; +} + +from_prim_ints!(i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize); + +impl From for Number { + fn from(f: f32) -> Self { + Self::Float(f as f64) + } +} + +impl From for Number { + fn from(f: f64) -> Self { + Self::Float(f) + } +} + +impl From for Number { + fn from(v: Decimal) -> Self { + Self::Decimal(v) + } +} + +impl FromStr for Number { + type Err = (); + fn from_str(s: &str) -> Result { + Self::try_from(s) + } +} + +impl TryFrom for Number { + type Error = (); + fn try_from(v: String) -> Result { + Self::try_from(v.as_str()) + } +} + +impl TryFrom for Number { + type Error = (); + fn try_from(v: Strand) -> Result { + Self::try_from(v.as_str()) + } +} + +impl TryFrom<&str> for Number { + type Error = (); + fn try_from(v: &str) -> Result { + // Attempt to parse as i64 + match v.parse::() { + // Store it as an i64 + Ok(v) => Ok(Self::Int(v)), + // It wasn't parsed as a i64 so parse as a float + _ => match f64::from_str(v) { + // Store it as a float + Ok(v) => Ok(Self::Float(v)), + // It wasn't parsed as a number + _ => Err(()), + }, + } + } +} + +macro_rules! try_into_prim { + // TODO: switch to one argument per int once https://github.com/rust-lang/rust/issues/29599 is stable + ($($int: ty => $to_int: ident),*) => { + $( + impl TryFrom for $int { + type Error = Error; + fn try_from(value: Number) -> Result { + match value { + Number::Int(v) => match v.$to_int() { + Some(v) => Ok(v), + None => Err(Error::TryFrom(value.to_string(), stringify!($int))), + }, + Number::Float(v) => match v.$to_int() { + Some(v) => Ok(v), + None => Err(Error::TryFrom(value.to_string(), stringify!($int))), + }, + Number::Decimal(ref v) => match v.$to_int() { + Some(v) => Ok(v), + None => Err(Error::TryFrom(value.to_string(), stringify!($int))), + }, + } + } + } + )* + }; +} + +try_into_prim!( + i8 => to_i8, i16 => to_i16, i32 => to_i32, i64 => to_i64, i128 => to_i128, + u8 => to_u8, u16 => to_u16, u32 => to_u32, u64 => to_u64, u128 => to_u128, + f32 => to_f32, f64 => to_f64 +); + +impl TryFrom for Decimal { + type Error = Error; + fn try_from(value: Number) -> Result { + match value { + Number::Int(v) => match Decimal::from_i64(v) { + Some(v) => Ok(v), + None => Err(Error::TryFrom(value.to_string(), "Decimal")), + }, + Number::Float(v) => match Decimal::try_from(v) { + Ok(v) => Ok(v), + _ => Err(Error::TryFrom(value.to_string(), "Decimal")), + }, + Number::Decimal(x) => Ok(x), + } + } +} + +impl Display for Number { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + Number::Int(v) => Display::fmt(v, f), + Number::Float(v) => { + if v.is_finite() { + // Add suffix to distinguish between int and float + write!(f, "{v}f") + } else { + // Don't add suffix for NaN, inf, -inf + Display::fmt(v, f) + } + } + Number::Decimal(v) => write!(f, "{v}dec"), + } + } +} + +impl Number { + // ----------------------------------- + // Constants + // ----------------------------------- + + pub const NAN: Number = Number::Float(f64::NAN); + + // ----------------------------------- + // Simple number detection + // ----------------------------------- + + pub fn is_nan(&self) -> bool { + matches!(self, Number::Float(v) if v.is_nan()) + } + + pub fn is_int(&self) -> bool { + matches!(self, Number::Int(_)) + } + + pub fn is_float(&self) -> bool { + matches!(self, Number::Float(_)) + } + + pub fn is_decimal(&self) -> bool { + matches!(self, Number::Decimal(_)) + } + + pub fn is_integer(&self) -> bool { + match self { + Number::Int(_) => true, + Number::Float(v) => v.fract() == 0.0, + Number::Decimal(v) => v.is_integer(), + } + } + + pub fn is_truthy(&self) -> bool { + match self { + Number::Int(v) => v != &0, + Number::Float(v) => v != &0.0, + Number::Decimal(v) => v != &Decimal::ZERO, + } + } + + pub fn is_positive(&self) -> bool { + match self { + Number::Int(v) => v > &0, + Number::Float(v) => v > &0.0, + Number::Decimal(v) => v > &Decimal::ZERO, + } + } + + pub fn is_negative(&self) -> bool { + match self { + Number::Int(v) => v < &0, + Number::Float(v) => v < &0.0, + Number::Decimal(v) => v < &Decimal::ZERO, + } + } + + pub fn is_zero(&self) -> bool { + match self { + Number::Int(v) => v == &0, + Number::Float(v) => v == &0.0, + Number::Decimal(v) => v == &Decimal::ZERO, + } + } + + pub fn is_zero_or_positive(&self) -> bool { + match self { + Number::Int(v) => v >= &0, + Number::Float(v) => v >= &0.0, + Number::Decimal(v) => v >= &Decimal::ZERO, + } + } + + pub fn is_zero_or_negative(&self) -> bool { + match self { + Number::Int(v) => v <= &0, + Number::Float(v) => v <= &0.0, + Number::Decimal(v) => v <= &Decimal::ZERO, + } + } + + // ----------------------------------- + // Simple conversion of number + // ----------------------------------- + + pub fn as_usize(self) -> usize { + match self { + Number::Int(v) => v as usize, + Number::Float(v) => v as usize, + Number::Decimal(v) => v.try_into().unwrap_or_default(), + } + } + + pub fn as_int(self) -> i64 { + match self { + Number::Int(v) => v, + Number::Float(v) => v as i64, + Number::Decimal(v) => v.try_into().unwrap_or_default(), + } + } + + pub fn as_float(self) -> f64 { + match self { + Number::Int(v) => v as f64, + Number::Float(v) => v, + Number::Decimal(v) => v.try_into().unwrap_or_default(), + } + } + + pub fn as_decimal(self) -> Decimal { + match self { + Number::Int(v) => Decimal::from(v), + Number::Float(v) => Decimal::try_from(v).unwrap_or_default(), + Number::Decimal(v) => v, + } + } + + // ----------------------------------- + // Complex conversion of number + // ----------------------------------- + + pub fn to_usize(&self) -> usize { + match self { + Number::Int(v) => *v as usize, + Number::Float(v) => *v as usize, + Number::Decimal(v) => v.to_usize().unwrap_or_default(), + } + } + + pub fn to_int(&self) -> i64 { + match self { + Number::Int(v) => *v, + Number::Float(v) => *v as i64, + Number::Decimal(v) => v.to_i64().unwrap_or_default(), + } + } + + pub fn to_float(&self) -> f64 { + match self { + Number::Int(v) => *v as f64, + Number::Float(v) => *v, + &Number::Decimal(v) => v.try_into().unwrap_or_default(), + } + } + + pub fn to_decimal(&self) -> Decimal { + match self { + // #[allow(clippy::unnecessary_fallible_conversions)] // `Decimal::from` can panic + // `clippy::unnecessary_fallible_conversions` not available on Rust < v1.75 + #[allow(warnings)] + Number::Int(v) => Decimal::try_from(*v).unwrap_or_default(), + Number::Float(v) => Decimal::try_from(*v).unwrap_or_default(), + Number::Decimal(v) => *v, + } + } + + // ----------------------------------- + // + // ----------------------------------- + + pub fn abs(self) -> Self { + match self { + Number::Int(v) => v.abs().into(), + Number::Float(v) => v.abs().into(), + Number::Decimal(v) => v.abs().into(), + } + } + + pub fn acos(self) -> Self { + self.to_float().acos().into() + } + + pub fn ceil(self) -> Self { + match self { + Number::Int(v) => v.into(), + Number::Float(v) => v.ceil().into(), + Number::Decimal(v) => v.ceil().into(), + } + } + + pub fn floor(self) -> Self { + match self { + Number::Int(v) => v.into(), + Number::Float(v) => v.floor().into(), + Number::Decimal(v) => v.floor().into(), + } + } + + pub fn round(self) -> Self { + match self { + Number::Int(v) => v.into(), + Number::Float(v) => v.round().into(), + Number::Decimal(v) => v.round().into(), + } + } + + pub fn fixed(self, precision: usize) -> Number { + match self { + Number::Int(v) => format!("{v:.precision$}").try_into().unwrap_or_default(), + Number::Float(v) => format!("{v:.precision$}").try_into().unwrap_or_default(), + Number::Decimal(v) => v.round_dp(precision as u32).into(), + } + } + + pub fn sqrt(self) -> Self { + match self { + Number::Int(v) => (v as f64).sqrt().into(), + Number::Float(v) => v.sqrt().into(), + Number::Decimal(v) => v.sqrt().unwrap_or_default().into(), + } + } + + pub fn pow(self, power: Number) -> Number { + match (self, power) { + (Number::Int(v), Number::Int(p)) => Number::Int(v.pow(p as u32)), + (Number::Decimal(v), Number::Int(p)) => v.powi(p).into(), + // TODO: (Number::Decimal(v), Number::Float(p)) => todo!(), + // TODO: (Number::Decimal(v), Number::Decimal(p)) => todo!(), + (v, p) => v.as_float().powf(p.as_float()).into(), + } + } +} + +impl Eq for Number {} + +impl Ord for Number { + fn cmp(&self, other: &Self) -> Ordering { + fn total_cmp_f64(a: f64, b: f64) -> Ordering { + if a == 0.0 && b == 0.0 { + // -0.0 = 0.0 + Ordering::Equal + } else { + // Handles NaN's + a.total_cmp(&b) + } + } + + match (self, other) { + (Number::Int(v), Number::Int(w)) => v.cmp(w), + (Number::Float(v), Number::Float(w)) => total_cmp_f64(*v, *w), + (Number::Decimal(v), Number::Decimal(w)) => v.cmp(w), + // ------------------------------ + (Number::Int(v), Number::Float(w)) => total_cmp_f64(*v as f64, *w), + (Number::Float(v), Number::Int(w)) => total_cmp_f64(*v, *w as f64), + // ------------------------------ + (Number::Int(v), Number::Decimal(w)) => Decimal::from(*v).cmp(w), + (Number::Decimal(v), Number::Int(w)) => v.cmp(&Decimal::from(*w)), + // ------------------------------ + (Number::Float(v), Number::Decimal(w)) => { + // `rust_decimal::Decimal` code comments indicate that `to_f64` is infallible + total_cmp_f64(*v, w.to_f64().unwrap()) + } + (Number::Decimal(v), Number::Float(w)) => total_cmp_f64(v.to_f64().unwrap(), *w), + } + } +} + +// Warning: Equal numbers may have different hashes, which violates +// the invariants of certain collections! +impl hash::Hash for Number { + fn hash(&self, state: &mut H) { + match self { + Number::Int(v) => v.hash(state), + Number::Float(v) => v.to_bits().hash(state), + Number::Decimal(v) => v.hash(state), + } + } +} + +impl PartialEq for Number { + fn eq(&self, other: &Self) -> bool { + fn total_eq_f64(a: f64, b: f64) -> bool { + a.to_bits().eq(&b.to_bits()) || (a == 0.0 && b == 0.0) + } + + match (self, other) { + (Number::Int(v), Number::Int(w)) => v.eq(w), + (Number::Float(v), Number::Float(w)) => total_eq_f64(*v, *w), + (Number::Decimal(v), Number::Decimal(w)) => v.eq(w), + // ------------------------------ + (Number::Int(v), Number::Float(w)) => total_eq_f64(*v as f64, *w), + (Number::Float(v), Number::Int(w)) => total_eq_f64(*v, *w as f64), + // ------------------------------ + (Number::Int(v), Number::Decimal(w)) => Decimal::from(*v).eq(w), + (Number::Decimal(v), Number::Int(w)) => v.eq(&Decimal::from(*w)), + // ------------------------------ + (Number::Float(v), Number::Decimal(w)) => total_eq_f64(*v, w.to_f64().unwrap()), + (Number::Decimal(v), Number::Float(w)) => total_eq_f64(v.to_f64().unwrap(), *w), + } + } +} + +impl PartialOrd for Number { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +macro_rules! impl_simple_try_op { + ($trt:ident, $fn:ident, $unchecked:ident, $checked:ident) => { + impl $trt for Number { + type Output = Self; + fn $fn(self, other: Self) -> Result { + Ok(match (self, other) { + (Number::Int(v), Number::Int(w)) => Number::Int( + v.$checked(w).ok_or_else(|| Error::$trt(v.to_string(), w.to_string()))?, + ), + (Number::Float(v), Number::Float(w)) => Number::Float(v.$unchecked(w)), + (Number::Decimal(v), Number::Decimal(w)) => Number::Decimal( + v.$checked(w).ok_or_else(|| Error::$trt(v.to_string(), w.to_string()))?, + ), + (Number::Int(v), Number::Float(w)) => Number::Float((v as f64).$unchecked(w)), + (Number::Float(v), Number::Int(w)) => Number::Float(v.$unchecked(w as f64)), + (v, w) => Number::Decimal( + v.to_decimal() + .$checked(w.to_decimal()) + .ok_or_else(|| Error::$trt(v.to_string(), w.to_string()))?, + ), + }) + } + } + }; +} + +impl_simple_try_op!(TryAdd, try_add, add, checked_add); +impl_simple_try_op!(TrySub, try_sub, sub, checked_sub); +impl_simple_try_op!(TryMul, try_mul, mul, checked_mul); +impl_simple_try_op!(TryDiv, try_div, div, checked_div); +impl_simple_try_op!(TryRem, try_rem, rem, checked_rem); + +impl TryPow for Number { + type Output = Self; + fn try_pow(self, power: Self) -> Result { + Ok(match (self, power) { + (Self::Int(v), Self::Int(p)) => Self::Int(match v { + 0 => match p.cmp(&0) { + // 0^(-x) + Ordering::Less => return Err(Error::TryPow(v.to_string(), p.to_string())), + // 0^0 + Ordering::Equal => 1, + // 0^x + Ordering::Greater => 0, + }, + // 1^p + 1 => 1, + -1 => { + if p % 2 == 0 { + // (-1)^even + 1 + } else { + // (-1)^odd + -1 + } + } + // try_into may cause an error, which would be wrong for the above cases. + _ => p + .try_into() + .ok() + .and_then(|p| v.checked_pow(p)) + .ok_or_else(|| Error::TryPow(v.to_string(), p.to_string()))?, + }), + (Self::Decimal(v), Self::Int(p)) => Self::Decimal( + v.checked_powi(p).ok_or_else(|| Error::TryPow(v.to_string(), p.to_string()))?, + ), + (Self::Decimal(v), Self::Float(p)) => Self::Decimal( + v.checked_powf(p).ok_or_else(|| Error::TryPow(v.to_string(), p.to_string()))?, + ), + (Self::Decimal(v), Self::Decimal(p)) => Self::Decimal( + v.checked_powd(p).ok_or_else(|| Error::TryPow(v.to_string(), p.to_string()))?, + ), + (v, p) => v.as_float().powf(p.as_float()).into(), + }) + } +} + +impl TryNeg for Number { + type Output = Self; + + fn try_neg(self) -> Result { + Ok(match self { + Self::Int(n) => { + Number::Int(n.checked_neg().ok_or_else(|| Error::TryNeg(n.to_string()))?) + } + Self::Float(n) => Number::Float(-n), + Self::Decimal(n) => Number::Decimal(-n), + }) + } +} + +impl ops::Add for Number { + type Output = Self; + fn add(self, other: Self) -> Self { + match (self, other) { + (Number::Int(v), Number::Int(w)) => Number::Int(v + w), + (Number::Float(v), Number::Float(w)) => Number::Float(v + w), + (Number::Decimal(v), Number::Decimal(w)) => Number::Decimal(v + w), + (Number::Int(v), Number::Float(w)) => Number::Float(v as f64 + w), + (Number::Float(v), Number::Int(w)) => Number::Float(v + w as f64), + (v, w) => Number::from(v.as_decimal() + w.as_decimal()), + } + } +} + +impl<'a, 'b> ops::Add<&'b Number> for &'a Number { + type Output = Number; + fn add(self, other: &'b Number) -> Number { + match (self, other) { + (Number::Int(v), Number::Int(w)) => Number::Int(v + w), + (Number::Float(v), Number::Float(w)) => Number::Float(v + w), + (Number::Decimal(v), Number::Decimal(w)) => Number::Decimal(v + w), + (Number::Int(v), Number::Float(w)) => Number::Float(*v as f64 + w), + (Number::Float(v), Number::Int(w)) => Number::Float(v + *w as f64), + (v, w) => Number::from(v.to_decimal() + w.to_decimal()), + } + } +} + +impl ops::Sub for Number { + type Output = Self; + fn sub(self, other: Self) -> Self { + match (self, other) { + (Number::Int(v), Number::Int(w)) => Number::Int(v - w), + (Number::Float(v), Number::Float(w)) => Number::Float(v - w), + (Number::Decimal(v), Number::Decimal(w)) => Number::Decimal(v - w), + (Number::Int(v), Number::Float(w)) => Number::Float(v as f64 - w), + (Number::Float(v), Number::Int(w)) => Number::Float(v - w as f64), + (v, w) => Number::from(v.as_decimal() - w.as_decimal()), + } + } +} + +impl<'a, 'b> ops::Sub<&'b Number> for &'a Number { + type Output = Number; + fn sub(self, other: &'b Number) -> Number { + match (self, other) { + (Number::Int(v), Number::Int(w)) => Number::Int(v - w), + (Number::Float(v), Number::Float(w)) => Number::Float(v - w), + (Number::Decimal(v), Number::Decimal(w)) => Number::Decimal(v - w), + (Number::Int(v), Number::Float(w)) => Number::Float(*v as f64 - w), + (Number::Float(v), Number::Int(w)) => Number::Float(v - *w as f64), + (v, w) => Number::from(v.to_decimal() - w.to_decimal()), + } + } +} + +impl ops::Mul for Number { + type Output = Self; + fn mul(self, other: Self) -> Self { + match (self, other) { + (Number::Int(v), Number::Int(w)) => Number::Int(v * w), + (Number::Float(v), Number::Float(w)) => Number::Float(v * w), + (Number::Decimal(v), Number::Decimal(w)) => Number::Decimal(v * w), + (Number::Int(v), Number::Float(w)) => Number::Float(v as f64 * w), + (Number::Float(v), Number::Int(w)) => Number::Float(v * w as f64), + (v, w) => Number::from(v.as_decimal() * w.as_decimal()), + } + } +} + +impl<'a, 'b> ops::Mul<&'b Number> for &'a Number { + type Output = Number; + fn mul(self, other: &'b Number) -> Number { + match (self, other) { + (Number::Int(v), Number::Int(w)) => Number::Int(v * w), + (Number::Float(v), Number::Float(w)) => Number::Float(v * w), + (Number::Decimal(v), Number::Decimal(w)) => Number::Decimal(v * w), + (Number::Int(v), Number::Float(w)) => Number::Float(*v as f64 * w), + (Number::Float(v), Number::Int(w)) => Number::Float(v * *w as f64), + (v, w) => Number::from(v.to_decimal() * w.to_decimal()), + } + } +} + +impl ops::Div for Number { + type Output = Self; + fn div(self, other: Self) -> Self { + match (self, other) { + (Number::Int(v), Number::Int(w)) => Number::Int(v / w), + (Number::Float(v), Number::Float(w)) => Number::Float(v / w), + (Number::Decimal(v), Number::Decimal(w)) => Number::Decimal(v / w), + (Number::Int(v), Number::Float(w)) => Number::Float(v as f64 / w), + (Number::Float(v), Number::Int(w)) => Number::Float(v / w as f64), + (v, w) => Number::from(v.as_decimal() / w.as_decimal()), + } + } +} + +impl<'a, 'b> ops::Div<&'b Number> for &'a Number { + type Output = Number; + fn div(self, other: &'b Number) -> Number { + match (self, other) { + (Number::Int(v), Number::Int(w)) => Number::Int(v / w), + (Number::Float(v), Number::Float(w)) => Number::Float(v / w), + (Number::Decimal(v), Number::Decimal(w)) => Number::Decimal(v / w), + (Number::Int(v), Number::Float(w)) => Number::Float(*v as f64 / w), + (Number::Float(v), Number::Int(w)) => Number::Float(v / *w as f64), + (v, w) => Number::from(v.to_decimal() / w.to_decimal()), + } + } +} + +impl Neg for Number { + type Output = Self; + + fn neg(self) -> Self::Output { + match self { + Self::Int(n) => Number::Int(-n), + Self::Float(n) => Number::Float(-n), + Self::Decimal(n) => Number::Decimal(-n), + } + } +} + +// ------------------------------ + +impl Sum for Number { + fn sum(iter: I) -> Number + where + I: Iterator, + { + iter.fold(Number::Int(0), |a, b| a + b) + } +} + +impl<'a> Sum<&'a Self> for Number { + fn sum(iter: I) -> Number + where + I: Iterator, + { + iter.fold(Number::Int(0), |a, b| &a + b) + } +} + +impl Product for Number { + fn product(iter: I) -> Number + where + I: Iterator, + { + iter.fold(Number::Int(1), |a, b| a * b) + } +} + +impl<'a> Product<&'a Self> for Number { + fn product(iter: I) -> Number + where + I: Iterator, + { + iter.fold(Number::Int(1), |a, b| &a * b) + } +} + +pub struct Sorted(pub T); + +pub trait Sort { + fn sorted(&mut self) -> Sorted<&Self> + where + Self: Sized; +} + +impl Sort for Vec { + fn sorted(&mut self) -> Sorted<&Vec> { + self.sort(); + Sorted(self) + } +} diff --git a/core/src/sql/v2/object.rs b/core/src/sql/v2/object.rs new file mode 100644 index 00000000..18ac0695 --- /dev/null +++ b/core/src/sql/v2/object.rs @@ -0,0 +1,322 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::{ + escape::escape_key, + fmt::{is_pretty, pretty_indent, Fmt, Pretty}, + Operation, Thing, Value, +}; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::fmt::{self, Display, Formatter, Write}; +use std::ops::Deref; +use std::ops::DerefMut; + +pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Object"; + +/// Invariant: Keys never contain NUL bytes. +#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[serde(rename = "$surrealdb::private::sql::Object")] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Object(#[serde(with = "no_nul_bytes_in_keys")] pub BTreeMap); + +impl From> for Object { + fn from(v: BTreeMap<&str, Value>) -> Self { + Self(v.into_iter().map(|(key, val)| (key.to_string(), val)).collect()) + } +} + +impl From> for Object { + fn from(v: BTreeMap) -> Self { + Self(v) + } +} + +impl From> for Object { + fn from(v: HashMap<&str, Value>) -> Self { + Self(v.into_iter().map(|(key, val)| (key.to_string(), val)).collect()) + } +} + +impl From> for Object { + fn from(v: HashMap) -> Self { + Self(v.into_iter().collect()) + } +} + +impl From> for Object { + fn from(v: Option) -> Self { + v.unwrap_or_default() + } +} + +impl From for Object { + fn from(v: Operation) -> Self { + Self(match v { + Operation::Add { + path, + value, + } => map! { + String::from("op") => Value::from("add"), + String::from("path") => path.to_path().into(), + String::from("value") => value + }, + Operation::Remove { + path, + } => map! { + String::from("op") => Value::from("remove"), + String::from("path") => path.to_path().into() + }, + Operation::Replace { + path, + value, + } => map! { + String::from("op") => Value::from("replace"), + String::from("path") => path.to_path().into(), + String::from("value") => value + }, + Operation::Change { + path, + value, + } => map! { + String::from("op") => Value::from("change"), + String::from("path") => path.to_path().into(), + String::from("value") => value + }, + Operation::Copy { + path, + from, + } => map! { + String::from("op") => Value::from("copy"), + String::from("path") => path.to_path().into(), + String::from("from") => from.to_path().into() + }, + Operation::Move { + path, + from, + } => map! { + String::from("op") => Value::from("move"), + String::from("path") => path.to_path().into(), + String::from("from") => from.to_path().into() + }, + Operation::Test { + path, + value, + } => map! { + String::from("op") => Value::from("test"), + String::from("path") => path.to_path().into(), + String::from("value") => value + }, + }) + } +} + +impl Deref for Object { + type Target = BTreeMap; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Object { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl IntoIterator for Object { + type Item = (String, Value); + type IntoIter = std::collections::btree_map::IntoIter; + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl Object { + /// Fetch the record id if there is one + pub fn rid(&self) -> Option { + match self.get("id") { + Some(Value::Thing(v)) => Some(v.clone()), + _ => None, + } + } + /// Convert this object to a diff-match-patch operation + pub fn to_operation(&self) -> Result { + match self.get("op") { + Some(op_val) => match self.get("path") { + Some(path_val) => { + let path = path_val.jsonpath(); + + let from = + self.get("from").map(|value| value.jsonpath()).ok_or(Error::InvalidPatch { + message: String::from("'from' key missing"), + }); + + let value = self.get("value").cloned().ok_or(Error::InvalidPatch { + message: String::from("'value' key missing"), + }); + + match op_val.clone().as_string().as_str() { + // Add operation + "add" => Ok(Operation::Add { + path, + value: value?, + }), + // Remove operation + "remove" => Ok(Operation::Remove { + path, + }), + // Replace operation + "replace" => Ok(Operation::Replace { + path, + value: value?, + }), + // Change operation + "change" => Ok(Operation::Change { + path, + value: value?, + }), + // Copy operation + "copy" => Ok(Operation::Copy { + path, + from: from?, + }), + // Move operation + "move" => Ok(Operation::Move { + path, + from: from?, + }), + // Test operation + "test" => Ok(Operation::Test { + path, + value: value?, + }), + unknown_op => Err(Error::InvalidPatch { + message: format!("unknown op '{unknown_op}'"), + }), + } + } + _ => Err(Error::InvalidPatch { + message: String::from("'path' key missing"), + }), + }, + _ => Err(Error::InvalidPatch { + message: String::from("'op' key missing"), + }), + } + } +} + +impl Object { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + ) -> Result { + let mut x = BTreeMap::new(); + for (k, v) in self.iter() { + match v.compute(ctx, opt, txn, doc).await { + Ok(v) => x.insert(k.clone(), v), + Err(e) => return Err(e), + }; + } + Ok(Value::Object(Object(x))) + } + + pub(crate) fn is_static(&self) -> bool { + self.values().all(Value::is_static) + } +} + +impl Display for Object { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let mut f = Pretty::from(f); + if is_pretty() { + f.write_char('{')?; + } else { + f.write_str("{ ")?; + } + if !self.is_empty() { + let indent = pretty_indent(); + write!( + f, + "{}", + Fmt::pretty_comma_separated( + self.0.iter().map(|args| Fmt::new(args, |(k, v), f| write!( + f, + "{}: {}", + escape_key(k), + v + ))), + ) + )?; + drop(indent); + } + if is_pretty() { + f.write_char('}') + } else { + f.write_str(" }") + } + } +} + +mod no_nul_bytes_in_keys { + use serde::{ + de::{self, Visitor}, + ser::SerializeMap, + Deserializer, Serializer, + }; + use std::{collections::BTreeMap, fmt}; + + use crate::sql::Value; + + pub(crate) fn serialize( + m: &BTreeMap, + serializer: S, + ) -> Result + where + S: Serializer, + { + let mut s = serializer.serialize_map(Some(m.len()))?; + for (k, v) in m { + debug_assert!(!k.contains('\0')); + s.serialize_entry(k, v)?; + } + s.end() + } + + pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + struct NoNulBytesInKeysVisitor; + + impl<'de> Visitor<'de> for NoNulBytesInKeysVisitor { + type Value = BTreeMap; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a map without any NUL bytes in its keys") + } + + fn visit_map(self, mut map: A) -> Result + where + A: de::MapAccess<'de>, + { + let mut ret = BTreeMap::new(); + while let Some((k, v)) = map.next_entry()? { + ret.insert(k, v); + } + Ok(ret) + } + } + + deserializer.deserialize_map(NoNulBytesInKeysVisitor) + } +} diff --git a/core/src/sql/v2/operation.rs b/core/src/sql/v2/operation.rs new file mode 100644 index 00000000..9304e0f1 --- /dev/null +++ b/core/src/sql/v2/operation.rs @@ -0,0 +1,38 @@ +use crate::sql::idiom::Idiom; +use crate::sql::value::Value; +use revision::revisioned; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[serde(tag = "op")] +#[serde(rename_all = "lowercase")] +#[revisioned(revision = 1)] +pub enum Operation { + Add { + path: Idiom, + value: Value, + }, + Remove { + path: Idiom, + }, + Replace { + path: Idiom, + value: Value, + }, + Change { + path: Idiom, + value: Value, + }, + Copy { + path: Idiom, + from: Idiom, + }, + Move { + path: Idiom, + from: Idiom, + }, + Test { + path: Idiom, + value: Value, + }, +} diff --git a/core/src/sql/v2/operator.rs b/core/src/sql/v2/operator.rs new file mode 100644 index 00000000..cc6f0c2e --- /dev/null +++ b/core/src/sql/v2/operator.rs @@ -0,0 +1,150 @@ +use crate::idx::ft::MatchRef; +use crate::sql::index::Distance; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; +use std::fmt::Write; + +/// Binary operators. +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub enum Operator { + // + Neg, // - + Not, // ! + // + Or, // || + And, // && + Tco, // ?: Ternary conditional operator + Nco, // ?? Null coalescing operator + // + Add, // + + Sub, // - + Mul, // * + Div, // / + Pow, // ** + Inc, // += + Dec, // -= + Ext, // +?= + // + Equal, // = + Exact, // == + NotEqual, // != + AllEqual, // *= + AnyEqual, // ?= + // + Like, // ~ + NotLike, // !~ + AllLike, // *~ + AnyLike, // ?~ + Matches(Option), // @{ref}@ + // + LessThan, // < + LessThanOrEqual, // <= + MoreThan, // > + MoreThanOrEqual, // >= + // + Contain, // ∋ + NotContain, // ∌ + ContainAll, // ⊇ + ContainAny, // ⊃ + ContainNone, // ⊅ + Inside, // ∈ + NotInside, // ∉ + AllInside, // ⊆ + AnyInside, // ⊂ + NoneInside, // ⊄ + // + Outside, + Intersects, + // + Knn(u32, Option), // <{k}[,{dist}]> + // + Rem, // % +} + +impl Default for Operator { + fn default() -> Self { + Self::Equal + } +} + +impl Operator { + #[inline] + pub fn precedence(&self) -> u8 { + match self { + Self::Or => 1, + Self::And => 2, + Self::Tco => 3, + Self::Nco => 4, + Self::Sub => 6, + Self::Add => 7, + Self::Mul => 8, + Self::Div => 9, + Self::Rem => 10, + _ => 5, + } + } +} + +impl fmt::Display for Operator { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Neg => f.write_str("-"), + Self::Not => f.write_str("!"), + Self::Or => f.write_str("OR"), + Self::And => f.write_str("AND"), + Self::Tco => f.write_str("?:"), + Self::Nco => f.write_str("??"), + Self::Add => f.write_str("+"), + Self::Sub => f.write_char('-'), + Self::Mul => f.write_char('*'), + Self::Div => f.write_char('/'), + Self::Rem => f.write_char('%'), + Self::Pow => f.write_str("**"), + Self::Inc => f.write_str("+="), + Self::Dec => f.write_str("-="), + Self::Ext => f.write_str("+?="), + Self::Equal => f.write_char('='), + Self::Exact => f.write_str("=="), + Self::NotEqual => f.write_str("!="), + Self::AllEqual => f.write_str("*="), + Self::AnyEqual => f.write_str("?="), + Self::Like => f.write_char('~'), + Self::NotLike => f.write_str("!~"), + Self::AllLike => f.write_str("*~"), + Self::AnyLike => f.write_str("?~"), + Self::LessThan => f.write_char('<'), + Self::LessThanOrEqual => f.write_str("<="), + Self::MoreThan => f.write_char('>'), + Self::MoreThanOrEqual => f.write_str(">="), + Self::Contain => f.write_str("CONTAINS"), + Self::NotContain => f.write_str("CONTAINSNOT"), + Self::ContainAll => f.write_str("CONTAINSALL"), + Self::ContainAny => f.write_str("CONTAINSANY"), + Self::ContainNone => f.write_str("CONTAINSNONE"), + Self::Inside => f.write_str("INSIDE"), + Self::NotInside => f.write_str("NOTINSIDE"), + Self::AllInside => f.write_str("ALLINSIDE"), + Self::AnyInside => f.write_str("ANYINSIDE"), + Self::NoneInside => f.write_str("NONEINSIDE"), + Self::Outside => f.write_str("OUTSIDE"), + Self::Intersects => f.write_str("INTERSECTS"), + Self::Matches(reference) => { + if let Some(r) = reference { + write!(f, "@{r}@") + } else { + f.write_str("@@") + } + } + Self::Knn(k, dist) => { + if let Some(d) = dist { + write!(f, "<{k},{d}>") + } else { + write!(f, "<{k}>") + } + } + } + } +} diff --git a/core/src/sql/v2/order.rs b/core/src/sql/v2/order.rs new file mode 100644 index 00000000..61900398 --- /dev/null +++ b/core/src/sql/v2/order.rs @@ -0,0 +1,71 @@ +use crate::sql::fmt::Fmt; +use crate::sql::idiom::Idiom; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; +use std::ops::Deref; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Orders(pub Vec); + +impl Deref for Orders { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl IntoIterator for Orders { + type Item = Order; + type IntoIter = std::vec::IntoIter; + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl fmt::Display for Orders { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "ORDER BY {}", Fmt::comma_separated(&self.0)) + } +} + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Order { + pub order: Idiom, + pub random: bool, + pub collate: bool, + pub numeric: bool, + /// true if the direction is ascending + pub direction: bool, +} + +impl Deref for Order { + type Target = Idiom; + fn deref(&self) -> &Self::Target { + &self.order + } +} + +impl fmt::Display for Order { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.order)?; + if self.random { + write!(f, "RAND()")?; + } + if self.collate { + write!(f, " COLLATE")?; + } + if self.numeric { + write!(f, " NUMERIC")?; + } + match self.direction { + false => write!(f, " DESC")?, + true => (), + }; + Ok(()) + } +} diff --git a/core/src/sql/v2/output.rs b/core/src/sql/v2/output.rs new file mode 100644 index 00000000..76ce029a --- /dev/null +++ b/core/src/sql/v2/output.rs @@ -0,0 +1,36 @@ +use crate::sql::field::Fields; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub enum Output { + None, + Null, + Diff, + After, + Before, + Fields(Fields), +} + +impl Default for Output { + fn default() -> Self { + Self::None + } +} + +impl Display for Output { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("RETURN ")?; + match self { + Self::None => f.write_str("NONE"), + Self::Null => f.write_str("NULL"), + Self::Diff => f.write_str("DIFF"), + Self::After => f.write_str("AFTER"), + Self::Before => f.write_str("BEFORE"), + Self::Fields(v) => Display::fmt(v, f), + } + } +} diff --git a/core/src/sql/v2/param.rs b/core/src/sql/v2/param.rs new file mode 100644 index 00000000..5cd7d3eb --- /dev/null +++ b/core/src/sql/v2/param.rs @@ -0,0 +1,119 @@ +use crate::{ + ctx::Context, + dbs::{Options, Transaction}, + doc::CursorDoc, + err::Error, + iam::Action, + sql::{ident::Ident, value::Value, Permission}, +}; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::{fmt, ops::Deref, str}; + +pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Param"; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[serde(rename = "$surrealdb::private::sql::Param")] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct Param(pub Ident); + +impl From for Param { + fn from(v: Ident) -> Self { + Self(v) + } +} + +impl From for Param { + fn from(v: String) -> Self { + Self(v.into()) + } +} + +impl From<&str> for Param { + fn from(v: &str) -> Self { + Self(v.into()) + } +} + +impl Deref for Param { + type Target = Ident; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Param { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + ) -> Result { + // Find the variable by name + match self.as_str() { + // This is a special param + "this" | "self" => match doc { + // The base document exists + Some(v) => v.doc.compute(ctx, opt, txn, doc).await, + // The base document does not exist + None => Ok(Value::None), + }, + // This is a normal param + v => match ctx.value(v) { + // The param has been set locally + Some(v) => v.compute(ctx, opt, txn, doc).await, + // The param has not been set locally + None => { + // Check that a database is set to prevent a panic + opt.valid_for_db()?; + let val = { + // Claim transaction + let mut run = txn.lock().await; + // Get the param definition + run.get_and_cache_db_param(opt.ns(), opt.db(), v).await + }; + // Check if the param has been set globally + match val { + // The param has been set globally + Ok(val) => { + // Check permissions + if opt.check_perms(Action::View) { + match &val.permissions { + Permission::Full => (), + Permission::None => { + return Err(Error::ParamPermissions { + name: v.to_owned(), + }) + } + Permission::Specific(e) => { + // Disable permissions + let opt = &opt.new_with_perms(false); + // Process the PERMISSION clause + if !e.compute(ctx, opt, txn, doc).await?.is_truthy() { + return Err(Error::ParamPermissions { + name: v.to_owned(), + }); + } + } + } + } + // Return the computed value + val.value.compute(ctx, opt, txn, doc).await + } + // The param has not been set globally + Err(_) => Ok(Value::None), + } + } + }, + } + } +} + +impl fmt::Display for Param { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "${}", &self.0) + } +} diff --git a/core/src/sql/v2/part.rs b/core/src/sql/v2/part.rs new file mode 100644 index 00000000..3adb08fd --- /dev/null +++ b/core/src/sql/v2/part.rs @@ -0,0 +1,126 @@ +use crate::sql::{fmt::Fmt, strand::no_nul_bytes, Graph, Ident, Idiom, Number, Value}; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; +use std::str; + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub enum Part { + All, + Flatten, + Last, + First, + Field(Ident), + Index(Number), + Where(Value), + Graph(Graph), + Value(Value), + Start(Value), + Method(#[serde(with = "no_nul_bytes")] String, Vec), +} + +impl From for Part { + fn from(v: i32) -> Self { + Self::Index(v.into()) + } +} + +impl From for Part { + fn from(v: isize) -> Self { + Self::Index(v.into()) + } +} + +impl From for Part { + fn from(v: usize) -> Self { + Self::Index(v.into()) + } +} + +impl From for Part { + fn from(v: String) -> Self { + Self::Field(v.into()) + } +} + +impl From for Part { + fn from(v: Number) -> Self { + Self::Index(v) + } +} + +impl From for Part { + fn from(v: Ident) -> Self { + Self::Field(v) + } +} + +impl From for Part { + fn from(v: Graph) -> Self { + Self::Graph(v) + } +} + +impl From<&str> for Part { + fn from(v: &str) -> Self { + match v.parse::() { + Ok(v) => Self::from(v), + _ => Self::from(v.to_owned()), + } + } +} + +impl Part { + /// Check if we require a writeable transaction + pub(crate) fn writeable(&self) -> bool { + match self { + Part::Start(v) => v.writeable(), + Part::Where(v) => v.writeable(), + Part::Value(v) => v.writeable(), + Part::Method(_, v) => v.iter().any(Value::writeable), + _ => false, + } + } + /// Returns a yield if an alias is specified + pub(crate) fn alias(&self) -> Option<&Idiom> { + match self { + Part::Graph(v) => v.alias.as_ref(), + _ => None, + } + } +} + +impl fmt::Display for Part { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Part::All => f.write_str("[*]"), + Part::Last => f.write_str("[$]"), + Part::First => f.write_str("[0]"), + Part::Start(v) => write!(f, "{v}"), + Part::Field(v) => write!(f, ".{v}"), + Part::Flatten => f.write_str("…"), + Part::Index(v) => write!(f, "[{v}]"), + Part::Where(v) => write!(f, "[WHERE {v}]"), + Part::Graph(v) => write!(f, "{v}"), + Part::Value(v) => write!(f, "[{v}]"), + Part::Method(v, a) => write!(f, ".{v}({})", Fmt::comma_separated(a)), + } + } +} + +// ------------------------------ + +pub trait Next<'a> { + fn next(&'a self) -> &[Part]; +} + +impl<'a> Next<'a> for &'a [Part] { + fn next(&'a self) -> &'a [Part] { + match self.len() { + 0 => &[], + _ => &self[1..], + } + } +} diff --git a/core/src/sql/v2/paths.rs b/core/src/sql/v2/paths.rs new file mode 100644 index 00000000..eabbf656 --- /dev/null +++ b/core/src/sql/v2/paths.rs @@ -0,0 +1,26 @@ +use crate::sql::part::Part; +use once_cell::sync::Lazy; + +pub static ID: Lazy<[Part; 1]> = Lazy::new(|| [Part::from("id")]); + +pub static IP: Lazy<[Part; 1]> = Lazy::new(|| [Part::from("ip")]); + +pub static NS: Lazy<[Part; 1]> = Lazy::new(|| [Part::from("ns")]); + +pub static DB: Lazy<[Part; 1]> = Lazy::new(|| [Part::from("db")]); + +pub static SC: Lazy<[Part; 1]> = Lazy::new(|| [Part::from("sc")]); + +pub static SD: Lazy<[Part; 1]> = Lazy::new(|| [Part::from("sd")]); + +pub static OR: Lazy<[Part; 1]> = Lazy::new(|| [Part::from("or")]); + +pub static TK: Lazy<[Part; 1]> = Lazy::new(|| [Part::from("tk")]); + +pub static IN: Lazy<[Part; 1]> = Lazy::new(|| [Part::from("in")]); + +pub static OUT: Lazy<[Part; 1]> = Lazy::new(|| [Part::from("out")]); + +pub static META: Lazy<[Part; 1]> = Lazy::new(|| [Part::from("__")]); + +pub static EDGE: Lazy<[Part; 1]> = Lazy::new(|| [Part::from("__")]); diff --git a/core/src/sql/v2/permission.rs b/core/src/sql/v2/permission.rs new file mode 100644 index 00000000..7fb88c25 --- /dev/null +++ b/core/src/sql/v2/permission.rs @@ -0,0 +1,166 @@ +use crate::sql::fmt::is_pretty; +use crate::sql::fmt::pretty_indent; +use crate::sql::fmt::pretty_sequence_item; +use crate::sql::Value; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::Write; +use std::fmt::{self, Display, Formatter}; +use std::str; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct Permissions { + pub select: Permission, + pub create: Permission, + pub update: Permission, + pub delete: Permission, +} + +impl Permissions { + pub fn none() -> Self { + Permissions { + select: Permission::None, + create: Permission::None, + update: Permission::None, + delete: Permission::None, + } + } + + pub fn full() -> Self { + Permissions { + select: Permission::Full, + create: Permission::Full, + update: Permission::Full, + delete: Permission::Full, + } + } + + pub fn is_none(&self) -> bool { + self.select == Permission::None + && self.create == Permission::None + && self.update == Permission::None + && self.delete == Permission::None + } + + pub fn is_full(&self) -> bool { + self.select == Permission::Full + && self.create == Permission::Full + && self.update == Permission::Full + && self.delete == Permission::Full + } +} + +impl Display for Permissions { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "PERMISSIONS")?; + if self.is_none() { + return write!(f, " NONE"); + } + if self.is_full() { + return write!(f, " FULL"); + } + let mut lines = Vec::<(Vec, &Permission)>::new(); + for (c, permission) in [ + PermissionKind::Select, + PermissionKind::Create, + PermissionKind::Update, + PermissionKind::Delete, + ] + .into_iter() + .zip([&self.select, &self.create, &self.update, &self.delete]) + { + if let Some((existing, _)) = lines.iter_mut().find(|(_, p)| *p == permission) { + existing.push(c); + } else { + lines.push((vec![c], permission)); + } + } + let indent = if is_pretty() { + Some(pretty_indent()) + } else { + f.write_char(' ')?; + None + }; + for (i, (kinds, permission)) in lines.into_iter().enumerate() { + if i > 0 { + if is_pretty() { + pretty_sequence_item(); + } else { + f.write_str(", ")?; + } + } + write!(f, "FOR ")?; + for (i, kind) in kinds.into_iter().enumerate() { + if i > 0 { + f.write_str(", ")?; + } + f.write_str(kind.as_str())?; + } + match permission { + Permission::Specific(_) if is_pretty() => { + let _indent = pretty_indent(); + Display::fmt(permission, f)?; + } + _ => write!(f, " {permission}")?, + } + } + drop(indent); + Ok(()) + } +} + +#[derive(Clone, Copy, Eq, PartialEq, Debug)] +pub(crate) enum PermissionKind { + Select, + Create, + Update, + Delete, +} + +impl PermissionKind { + fn as_str(&self) -> &str { + match self { + PermissionKind::Select => "select", + PermissionKind::Create => "create", + PermissionKind::Update => "update", + PermissionKind::Delete => "delete", + } + } +} + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub enum Permission { + None, + Full, + Specific(Value), +} + +impl Default for Permission { + fn default() -> Self { + Self::Full + } +} + +impl Permission { + pub fn is_none(&self) -> bool { + matches!(self, Permission::None) + } + + pub fn is_full(&self) -> bool { + matches!(self, Permission::Full) + } +} + +impl Display for Permission { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + Self::None => f.write_str("NONE"), + Self::Full => f.write_str("FULL"), + Self::Specific(ref v) => write!(f, "WHERE {v}"), + } + } +} diff --git a/core/src/sql/v2/query.rs b/core/src/sql/v2/query.rs new file mode 100644 index 00000000..6293fd13 --- /dev/null +++ b/core/src/sql/v2/query.rs @@ -0,0 +1,51 @@ +use crate::sql::fmt::Pretty; +use crate::sql::statements::{DefineStatement, RemoveStatement}; +use crate::sql::{Statement, Statements}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::Write; +use std::fmt::{self, Display, Formatter}; +use std::ops::Deref; +use std::str; + +pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Query"; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[revisioned(revision = 1)] +#[serde(rename = "$surrealdb::private::sql::Query")] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Query(pub Statements); + +impl From for Query { + fn from(s: DefineStatement) -> Self { + Query(Statements(vec![Statement::Define(s)])) + } +} + +impl From for Query { + fn from(s: RemoveStatement) -> Self { + Query(Statements(vec![Statement::Remove(s)])) + } +} + +impl Deref for Query { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 .0 + } +} + +impl IntoIterator for Query { + type Item = Statement; + type IntoIter = std::vec::IntoIter; + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl Display for Query { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(Pretty::from(f), "{}", &self.0) + } +} diff --git a/core/src/sql/v2/range.rs b/core/src/sql/v2/range.rs new file mode 100644 index 00000000..e579c14c --- /dev/null +++ b/core/src/sql/v2/range.rs @@ -0,0 +1,141 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::{strand::no_nul_bytes, Id, Value}; +use crate::syn; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::cmp::Ordering; +use std::fmt; +use std::ops::Bound; +use std::str::FromStr; + +pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Range"; + +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)] +#[serde(rename = "$surrealdb::private::sql::Range")] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct Range { + #[serde(with = "no_nul_bytes")] + pub tb: String, + pub beg: Bound, + pub end: Bound, +} + +impl FromStr for Range { + type Err = (); + fn from_str(s: &str) -> Result { + Self::try_from(s) + } +} + +impl TryFrom<&str> for Range { + type Error = (); + fn try_from(v: &str) -> Result { + match syn::range(v) { + Ok(v) => Ok(v), + _ => Err(()), + } + } +} + +impl Range { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + ) -> Result { + Ok(Value::Range(Box::new(Range { + tb: self.tb.clone(), + beg: match &self.beg { + Bound::Included(id) => Bound::Included(id.compute(ctx, opt, txn, doc).await?), + Bound::Excluded(id) => Bound::Excluded(id.compute(ctx, opt, txn, doc).await?), + Bound::Unbounded => Bound::Unbounded, + }, + end: match &self.end { + Bound::Included(id) => Bound::Included(id.compute(ctx, opt, txn, doc).await?), + Bound::Excluded(id) => Bound::Excluded(id.compute(ctx, opt, txn, doc).await?), + Bound::Unbounded => Bound::Unbounded, + }, + }))) + } +} + +impl PartialOrd for Range { + fn partial_cmp(&self, other: &Self) -> Option { + match self.tb.partial_cmp(&other.tb) { + Some(Ordering::Equal) => match &self.beg { + Bound::Unbounded => match &other.beg { + Bound::Unbounded => Some(Ordering::Equal), + _ => Some(Ordering::Less), + }, + Bound::Included(v) => match &other.beg { + Bound::Unbounded => Some(Ordering::Greater), + Bound::Included(w) => match v.partial_cmp(w) { + Some(Ordering::Equal) => match &self.end { + Bound::Unbounded => match &other.end { + Bound::Unbounded => Some(Ordering::Equal), + _ => Some(Ordering::Greater), + }, + Bound::Included(v) => match &other.end { + Bound::Unbounded => Some(Ordering::Less), + Bound::Included(w) => v.partial_cmp(w), + _ => Some(Ordering::Greater), + }, + Bound::Excluded(v) => match &other.end { + Bound::Excluded(w) => v.partial_cmp(w), + _ => Some(Ordering::Less), + }, + }, + ordering => ordering, + }, + _ => Some(Ordering::Less), + }, + Bound::Excluded(v) => match &other.beg { + Bound::Excluded(w) => match v.partial_cmp(w) { + Some(Ordering::Equal) => match &self.end { + Bound::Unbounded => match &other.end { + Bound::Unbounded => Some(Ordering::Equal), + _ => Some(Ordering::Greater), + }, + Bound::Included(v) => match &other.end { + Bound::Unbounded => Some(Ordering::Less), + Bound::Included(w) => v.partial_cmp(w), + _ => Some(Ordering::Greater), + }, + Bound::Excluded(v) => match &other.end { + Bound::Excluded(w) => v.partial_cmp(w), + _ => Some(Ordering::Less), + }, + }, + ordering => ordering, + }, + _ => Some(Ordering::Greater), + }, + }, + ordering => ordering, + } + } +} + +impl fmt::Display for Range { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}:", self.tb)?; + match &self.beg { + Bound::Unbounded => write!(f, ""), + Bound::Included(id) => write!(f, "{id}"), + Bound::Excluded(id) => write!(f, "{id}>"), + }?; + match &self.end { + Bound::Unbounded => write!(f, ".."), + Bound::Excluded(id) => write!(f, "..{id}"), + Bound::Included(id) => write!(f, "..={id}"), + }?; + Ok(()) + } +} diff --git a/core/src/sql/v2/regex.rs b/core/src/sql/v2/regex.rs new file mode 100644 index 00000000..efbd175c --- /dev/null +++ b/core/src/sql/v2/regex.rs @@ -0,0 +1,150 @@ +use once_cell::sync::Lazy; +use quick_cache::sync::Cache; +use quick_cache::GuardResult; +use revision::revisioned; +use serde::{ + de::{self, Visitor}, + Deserialize, Deserializer, Serialize, Serializer, +}; +use std::cmp::Ordering; +use std::fmt::Debug; +use std::fmt::{self, Display, Formatter}; +use std::hash::{Hash, Hasher}; +use std::str::FromStr; +use std::{env, str}; + +pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Regex"; + +#[derive(Clone)] +#[revisioned(revision = 1)] +pub struct Regex(pub regex::Regex); + +impl Regex { + // Deref would expose `regex::Regex::as_str` which wouldn't have the '/' delimiters. + pub fn regex(&self) -> ®ex::Regex { + &self.0 + } +} + +fn regex_new(str: &str) -> Result { + static REGEX_CACHE: Lazy> = Lazy::new(|| { + let cache_size: usize = env::var("SURREAL_REGEX_CACHE_SIZE") + .map_or(1000, |v| v.parse().unwrap_or(1000)) + .max(10); // The minimum cache size is 10 + Cache::new(cache_size) + }); + match REGEX_CACHE.get_value_or_guard(str, None) { + GuardResult::Value(v) => Ok(v), + GuardResult::Guard(g) => { + let re = regex::Regex::new(str)?; + g.insert(re.clone()).ok(); + Ok(re) + } + GuardResult::Timeout => { + warn!("Regex cache timeout"); + regex::Regex::new(str) + } + } +} + +impl FromStr for Regex { + type Err = ::Err; + + fn from_str(s: &str) -> Result { + if s.contains('\0') { + Err(regex::Error::Syntax("regex contained NUL byte".to_owned())) + } else { + regex_new(&s.replace("\\/", "/")).map(Self) + } + } +} + +impl PartialEq for Regex { + fn eq(&self, other: &Self) -> bool { + self.0.as_str().eq(other.0.as_str()) + } +} + +impl Eq for Regex {} + +impl Ord for Regex { + fn cmp(&self, other: &Self) -> Ordering { + self.0.as_str().cmp(other.0.as_str()) + } +} + +impl PartialOrd for Regex { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Hash for Regex { + fn hash(&self, state: &mut H) { + self.0.as_str().hash(state); + } +} + +impl Debug for Regex { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + Display::fmt(self, f) + } +} + +impl Display for Regex { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "/{}/", &self.0) + } +} + +impl Serialize for Regex { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_newtype_struct(TOKEN, self.0.as_str()) + } +} + +impl<'de> Deserialize<'de> for Regex { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct RegexNewtypeVisitor; + + impl<'de> Visitor<'de> for RegexNewtypeVisitor { + type Value = Regex; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a regex newtype") + } + + fn visit_newtype_struct(self, deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct RegexVisitor; + + impl<'de> Visitor<'de> for RegexVisitor { + type Value = Regex; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a regex str") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + Regex::from_str(value).map_err(|_| de::Error::custom("invalid regex")) + } + } + + deserializer.deserialize_str(RegexVisitor) + } + } + + deserializer.deserialize_newtype_struct(TOKEN, RegexNewtypeVisitor) + } +} diff --git a/core/src/sql/v2/scoring.rs b/core/src/sql/v2/scoring.rs new file mode 100644 index 00000000..12722400 --- /dev/null +++ b/core/src/sql/v2/scoring.rs @@ -0,0 +1,72 @@ +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; +use std::hash::{Hash, Hasher}; + +#[derive(Clone, Debug, PartialOrd, Serialize, Deserialize)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub enum Scoring { + Bm { + k1: f32, + b: f32, + }, // BestMatching25 + Vs, // VectorSearch +} + +impl Eq for Scoring {} + +impl PartialEq for Scoring { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + ( + Scoring::Bm { + k1, + b, + }, + Scoring::Bm { + k1: other_k1, + b: other_b, + }, + ) => k1.to_bits() == other_k1.to_bits() && b.to_bits() == other_b.to_bits(), + (Scoring::Vs, Scoring::Vs) => true, + _ => false, + } + } +} + +impl Hash for Scoring { + fn hash(&self, state: &mut H) { + match self { + Scoring::Bm { + k1, + b, + } => { + k1.to_bits().hash(state); + b.to_bits().hash(state); + } + Scoring::Vs => 0.hash(state), + } + } +} + +impl Scoring { + pub(crate) fn bm25() -> Self { + Self::Bm { + k1: 1.2, + b: 0.75, + } + } +} + +impl fmt::Display for Scoring { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Bm { + k1, + b, + } => write!(f, "BM25({},{})", k1, b), + Self::Vs => f.write_str("VS"), + } + } +} diff --git a/core/src/sql/v2/script.rs b/core/src/sql/v2/script.rs new file mode 100644 index 00000000..2f1e6fa1 --- /dev/null +++ b/core/src/sql/v2/script.rs @@ -0,0 +1,36 @@ +use crate::sql::strand::no_nul_bytes; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Formatter}; +use std::ops::Deref; +use std::str; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Script(#[serde(with = "no_nul_bytes")] pub String); + +impl From for Script { + fn from(s: String) -> Self { + Self(s) + } +} + +impl From<&str> for Script { + fn from(s: &str) -> Self { + Self::from(String::from(s)) + } +} + +impl Deref for Script { + type Target = String; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Display for Script { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + Display::fmt(&self.0, f) + } +} diff --git a/core/src/sql/v2/serde.rs b/core/src/sql/v2/serde.rs new file mode 100644 index 00000000..0f6a9d23 --- /dev/null +++ b/core/src/sql/v2/serde.rs @@ -0,0 +1,28 @@ +use bincode::Options; +use bincode::Result; +use serde::{Deserialize, Serialize}; + +pub fn serialize(value: &T) -> Result> +where + T: Serialize, +{ + bincode::options() + .with_no_limit() + .with_little_endian() + .with_varint_encoding() + .reject_trailing_bytes() + .serialize(value) +} + +pub fn deserialize<'a, T>(bytes: &'a [u8]) -> Result +where + T: Deserialize<'a>, +{ + bincode::options() + .with_no_limit() + .with_little_endian() + .with_varint_encoding() + // Ignore extra fields so we can pull out the ID only from responses that fail to deserialise + .allow_trailing_bytes() + .deserialize(bytes) +} diff --git a/core/src/sql/v2/split.rs b/core/src/sql/v2/split.rs new file mode 100644 index 00000000..2e3f6013 --- /dev/null +++ b/core/src/sql/v2/split.rs @@ -0,0 +1,50 @@ +use crate::sql::fmt::Fmt; +use crate::sql::idiom::Idiom; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Formatter}; +use std::ops::Deref; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Splits(pub Vec); + +impl Deref for Splits { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl IntoIterator for Splits { + type Item = Split; + type IntoIter = std::vec::IntoIter; + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl fmt::Display for Splits { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "SPLIT ON {}", Fmt::comma_separated(&self.0)) + } +} + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Split(pub Idiom); + +impl Deref for Split { + type Target = Idiom; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Display for Split { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + Display::fmt(&self.0, f) + } +} diff --git a/core/src/sql/v2/start.rs b/core/src/sql/v2/start.rs new file mode 100644 index 00000000..b6356758 --- /dev/null +++ b/core/src/sql/v2/start.rs @@ -0,0 +1,41 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::number::Number; +use crate::sql::value::Value; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Start(pub Value); + +impl Start { + pub(crate) async fn process( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + ) -> Result { + match self.0.compute(ctx, opt, txn, doc).await { + // This is a valid starting number + Ok(Value::Number(Number::Int(v))) if v >= 0 => Ok(v as usize), + // An invalid value was specified + Ok(v) => Err(Error::InvalidStart { + value: v.as_string(), + }), + // A different error occurred + Err(e) => Err(e), + } + } +} + +impl fmt::Display for Start { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "START {}", self.0) + } +} diff --git a/core/src/sql/v2/statement.rs b/core/src/sql/v2/statement.rs new file mode 100644 index 00000000..2c46881e --- /dev/null +++ b/core/src/sql/v2/statement.rs @@ -0,0 +1,203 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::{ + fmt::{Fmt, Pretty}, + statements::{ + AnalyzeStatement, BeginStatement, BreakStatement, CancelStatement, CommitStatement, + ContinueStatement, CreateStatement, DefineStatement, DeleteStatement, ForeachStatement, + IfelseStatement, InfoStatement, InsertStatement, KillStatement, LiveStatement, + OptionStatement, OutputStatement, RelateStatement, RemoveStatement, SelectStatement, + SetStatement, ShowStatement, SleepStatement, ThrowStatement, UpdateStatement, UseStatement, + }, + value::Value, +}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::{ + fmt::{self, Display, Formatter, Write}, + ops::Deref, + time::Duration, +}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct Statements(pub Vec); + +impl Deref for Statements { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl IntoIterator for Statements { + type Item = Statement; + type IntoIter = std::vec::IntoIter; + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl Display for Statements { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt( + &Fmt::one_line_separated(self.0.iter().map(|v| Fmt::new(v, |v, f| write!(f, "{v};")))), + f, + ) + } +} + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub enum Statement { + Value(Value), + Analyze(AnalyzeStatement), + Begin(BeginStatement), + Break(BreakStatement), + Continue(ContinueStatement), + Cancel(CancelStatement), + Commit(CommitStatement), + Create(CreateStatement), + Define(DefineStatement), + Delete(DeleteStatement), + Foreach(ForeachStatement), + Ifelse(IfelseStatement), + Info(InfoStatement), + Insert(InsertStatement), + Kill(KillStatement), + Live(LiveStatement), + Option(OptionStatement), + Output(OutputStatement), + Relate(RelateStatement), + Remove(RemoveStatement), + Select(SelectStatement), + Set(SetStatement), + Show(ShowStatement), + Sleep(SleepStatement), + Update(UpdateStatement), + Throw(ThrowStatement), + Use(UseStatement), +} + +impl Statement { + /// Get the statement timeout duration, if any + pub fn timeout(&self) -> Option { + match self { + Self::Create(v) => v.timeout.as_ref().map(|v| *v.0), + Self::Delete(v) => v.timeout.as_ref().map(|v| *v.0), + Self::Insert(v) => v.timeout.as_ref().map(|v| *v.0), + Self::Relate(v) => v.timeout.as_ref().map(|v| *v.0), + Self::Select(v) => v.timeout.as_ref().map(|v| *v.0), + Self::Update(v) => v.timeout.as_ref().map(|v| *v.0), + _ => None, + } + } + /// Check if we require a writeable transaction + pub(crate) fn writeable(&self) -> bool { + match self { + Self::Value(v) => v.writeable(), + Self::Analyze(_) => false, + Self::Break(_) => false, + Self::Continue(_) => false, + Self::Create(v) => v.writeable(), + Self::Define(_) => true, + Self::Delete(v) => v.writeable(), + Self::Foreach(v) => v.writeable(), + Self::Ifelse(v) => v.writeable(), + Self::Info(_) => false, + Self::Insert(v) => v.writeable(), + Self::Kill(_) => true, + Self::Live(_) => true, + Self::Output(v) => v.writeable(), + Self::Option(_) => false, + Self::Relate(v) => v.writeable(), + Self::Remove(_) => true, + Self::Select(v) => v.writeable(), + Self::Set(v) => v.writeable(), + Self::Show(_) => false, + Self::Sleep(_) => false, + Self::Throw(_) => false, + Self::Update(v) => v.writeable(), + Self::Use(_) => false, + _ => unreachable!(), + } + } + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + ) -> Result { + match self { + Self::Analyze(v) => v.compute(ctx, opt, txn, doc).await, + Self::Break(v) => v.compute(ctx, opt, txn, doc).await, + Self::Continue(v) => v.compute(ctx, opt, txn, doc).await, + Self::Create(v) => v.compute(ctx, opt, txn, doc).await, + Self::Delete(v) => v.compute(ctx, opt, txn, doc).await, + Self::Define(v) => v.compute(ctx, opt, txn, doc).await, + Self::Foreach(v) => v.compute(ctx, opt, txn, doc).await, + Self::Ifelse(v) => v.compute(ctx, opt, txn, doc).await, + Self::Info(v) => v.compute(ctx, opt, txn, doc).await, + Self::Insert(v) => v.compute(ctx, opt, txn, doc).await, + Self::Kill(v) => v.compute(ctx, opt, txn, doc).await, + Self::Live(v) => v.compute(ctx, opt, txn, doc).await, + Self::Output(v) => v.compute(ctx, opt, txn, doc).await, + Self::Relate(v) => v.compute(ctx, opt, txn, doc).await, + Self::Remove(v) => v.compute(ctx, opt, txn, doc).await, + Self::Select(v) => v.compute(ctx, opt, txn, doc).await, + Self::Set(v) => v.compute(ctx, opt, txn, doc).await, + Self::Show(v) => v.compute(ctx, opt, txn, doc).await, + Self::Sleep(v) => v.compute(ctx, opt, txn, doc).await, + Self::Throw(v) => v.compute(ctx, opt, txn, doc).await, + Self::Update(v) => v.compute(ctx, opt, txn, doc).await, + Self::Value(v) => { + // Ensure futures are processed + let opt = &opt.new_with_futures(true); + // Process the output value + v.compute(ctx, opt, txn, doc).await + } + _ => unreachable!(), + } + } +} + +impl Display for Statement { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + Self::Value(v) => write!(Pretty::from(f), "{v}"), + Self::Analyze(v) => write!(Pretty::from(f), "{v}"), + Self::Begin(v) => write!(Pretty::from(f), "{v}"), + Self::Break(v) => write!(Pretty::from(f), "{v}"), + Self::Cancel(v) => write!(Pretty::from(f), "{v}"), + Self::Commit(v) => write!(Pretty::from(f), "{v}"), + Self::Continue(v) => write!(Pretty::from(f), "{v}"), + Self::Create(v) => write!(Pretty::from(f), "{v}"), + Self::Define(v) => write!(Pretty::from(f), "{v}"), + Self::Delete(v) => write!(Pretty::from(f), "{v}"), + Self::Foreach(v) => write!(Pretty::from(f), "{v}"), + Self::Insert(v) => write!(Pretty::from(f), "{v}"), + Self::Ifelse(v) => write!(Pretty::from(f), "{v}"), + Self::Info(v) => write!(Pretty::from(f), "{v}"), + Self::Kill(v) => write!(Pretty::from(f), "{v}"), + Self::Live(v) => write!(Pretty::from(f), "{v}"), + Self::Option(v) => write!(Pretty::from(f), "{v}"), + Self::Output(v) => write!(Pretty::from(f), "{v}"), + Self::Relate(v) => write!(Pretty::from(f), "{v}"), + Self::Remove(v) => write!(Pretty::from(f), "{v}"), + Self::Select(v) => write!(Pretty::from(f), "{v}"), + Self::Set(v) => write!(Pretty::from(f), "{v}"), + Self::Show(v) => write!(Pretty::from(f), "{v}"), + Self::Sleep(v) => write!(Pretty::from(f), "{v}"), + Self::Throw(v) => write!(Pretty::from(f), "{v}"), + Self::Update(v) => write!(Pretty::from(f), "{v}"), + Self::Use(v) => write!(Pretty::from(f), "{v}"), + } + } +} diff --git a/core/src/sql/v2/statements/analyze.rs b/core/src/sql/v2/statements/analyze.rs new file mode 100644 index 00000000..6c5ecbab --- /dev/null +++ b/core/src/sql/v2/statements/analyze.rs @@ -0,0 +1,95 @@ +use crate::ctx::Context; +use crate::dbs::Options; +use crate::dbs::Transaction; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::iam::{Action, ResourceKind}; +use crate::idx::ft::FtIndex; +use crate::idx::trees::mtree::MTreeIndex; +use crate::idx::IndexKeyBase; +use crate::kvs::TransactionType; +use crate::sql::ident::Ident; +use crate::sql::index::Index; +use crate::sql::value::Value; +use crate::sql::Base; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; +use std::fmt::{Display, Formatter}; + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub enum AnalyzeStatement { + Idx(Ident, Ident), +} + +impl AnalyzeStatement { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + _doc: Option<&CursorDoc<'_>>, + ) -> Result { + match self { + AnalyzeStatement::Idx(tb, idx) => { + // Allowed to run? + opt.is_allowed(Action::View, ResourceKind::Index, &Base::Db)?; + // Read the index + let ix = txn + .lock() + .await + .get_and_cache_tb_index(opt.ns(), opt.db(), tb.as_str(), idx.as_str()) + .await?; + let ikb = IndexKeyBase::new(opt, &ix); + + // Index operation dispatching + let value: Value = match &ix.index { + Index::Search(p) => { + let ft = FtIndex::new( + ctx.get_index_stores(), + opt, + txn, + p.az.as_str(), + ikb, + p, + TransactionType::Read, + ) + .await?; + ft.statistics(txn).await?.into() + } + Index::MTree(p) => { + let mut tx = txn.lock().await; + let mt = MTreeIndex::new( + ctx.get_index_stores(), + &mut tx, + ikb, + p, + TransactionType::Read, + ) + .await?; + mt.statistics(&mut tx).await?.into() + } + _ => { + return Err(Error::FeatureNotYetImplemented { + feature: "Statistics on unique and non-unique indexes.".to_string(), + }) + } + }; + // Return the result object + Ok(value) + } + } + } +} + +impl Display for AnalyzeStatement { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + Self::Idx(tb, idx) => write!(f, "ANALYZE INDEX {idx} ON {tb}"), + } + } +} diff --git a/core/src/sql/v2/statements/begin.rs b/core/src/sql/v2/statements/begin.rs new file mode 100644 index 00000000..e57af359 --- /dev/null +++ b/core/src/sql/v2/statements/begin.rs @@ -0,0 +1,15 @@ +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct BeginStatement; + +impl fmt::Display for BeginStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("BEGIN TRANSACTION") + } +} diff --git a/core/src/sql/v2/statements/break.rs b/core/src/sql/v2/statements/break.rs new file mode 100644 index 00000000..a3d6e3f3 --- /dev/null +++ b/core/src/sql/v2/statements/break.rs @@ -0,0 +1,37 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::value::Value; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct BreakStatement; + +impl BreakStatement { + /// Check if we require a writeable transaction + pub(crate) fn writeable(&self) -> bool { + false + } + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + _ctx: &Context<'_>, + _opt: &Options, + _txn: &Transaction, + _doc: Option<&CursorDoc<'_>>, + ) -> Result { + Err(Error::Break) + } +} + +impl fmt::Display for BreakStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("BREAK") + } +} diff --git a/core/src/sql/v2/statements/cancel.rs b/core/src/sql/v2/statements/cancel.rs new file mode 100644 index 00000000..cd7a7670 --- /dev/null +++ b/core/src/sql/v2/statements/cancel.rs @@ -0,0 +1,15 @@ +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct CancelStatement; + +impl fmt::Display for CancelStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("CANCEL TRANSACTION") + } +} diff --git a/core/src/sql/v2/statements/commit.rs b/core/src/sql/v2/statements/commit.rs new file mode 100644 index 00000000..c1a3ae9b --- /dev/null +++ b/core/src/sql/v2/statements/commit.rs @@ -0,0 +1,15 @@ +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct CommitStatement; + +impl fmt::Display for CommitStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("COMMIT TRANSACTION") + } +} diff --git a/core/src/sql/v2/statements/continue.rs b/core/src/sql/v2/statements/continue.rs new file mode 100644 index 00000000..9bf2139a --- /dev/null +++ b/core/src/sql/v2/statements/continue.rs @@ -0,0 +1,37 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::Value; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct ContinueStatement; + +impl ContinueStatement { + /// Check if we require a writeable transaction + pub(crate) fn writeable(&self) -> bool { + false + } + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + _ctx: &Context<'_>, + _opt: &Options, + _txn: &Transaction, + _doc: Option<&CursorDoc<'_>>, + ) -> Result { + Err(Error::Continue) + } +} + +impl fmt::Display for ContinueStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("CONTINUE") + } +} diff --git a/core/src/sql/v2/statements/create.rs b/core/src/sql/v2/statements/create.rs new file mode 100644 index 00000000..e320dc6e --- /dev/null +++ b/core/src/sql/v2/statements/create.rs @@ -0,0 +1,93 @@ +use crate::ctx::Context; +use crate::dbs::{Iterator, Options, Statement, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::{Data, Output, Timeout, Value, Values}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[revisioned(revision = 2)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct CreateStatement { + #[revision(start = 2)] + pub only: bool, + pub what: Values, + pub data: Option, + pub output: Option, + pub timeout: Option, + pub parallel: bool, +} + +impl CreateStatement { + /// Check if we require a writeable transaction + pub(crate) fn writeable(&self) -> bool { + true + } + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + ) -> Result { + // Valid options? + opt.valid_for_db()?; + // Create a new iterator + let mut i = Iterator::new(); + // Assign the statement + let stm = Statement::from(self); + // Ensure futures are stored + let opt = &opt.new_with_futures(false); + // Loop over the create targets + for w in self.what.0.iter() { + let v = w.compute(ctx, opt, txn, doc).await?; + i.prepare(ctx, opt, txn, &stm, v).await.map_err(|e| match e { + Error::InvalidStatementTarget { + value: v, + } => Error::CreateStatement { + value: v, + }, + e => e, + })?; + } + // Output the results + match i.output(ctx, opt, txn, &stm).await? { + // This is a single record result + Value::Array(mut a) if self.only => match a.len() { + // There was exactly one result + 1 => Ok(a.remove(0)), + // There were no results + _ => Err(Error::SingleOnlyOutput), + }, + // This is standard query result + v => Ok(v), + } + } +} + +impl fmt::Display for CreateStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "CREATE")?; + if self.only { + f.write_str(" ONLY")? + } + write!(f, " {}", self.what)?; + if let Some(ref v) = self.data { + write!(f, " {v}")? + } + if let Some(ref v) = self.output { + write!(f, " {v}")? + } + if let Some(ref v) = self.timeout { + write!(f, " {v}")? + } + if self.parallel { + f.write_str(" PARALLEL")? + } + Ok(()) + } +} diff --git a/core/src/sql/v2/statements/define/analyzer.rs b/core/src/sql/v2/statements/define/analyzer.rs new file mode 100644 index 00000000..c2911acf --- /dev/null +++ b/core/src/sql/v2/statements/define/analyzer.rs @@ -0,0 +1,69 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::iam::{Action, ResourceKind}; +use crate::sql::{filter::Filter, tokenizer::Tokenizer, Base, Ident, Strand, Value}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 2)] +pub struct DefineAnalyzerStatement { + pub name: Ident, + #[revision(start = 2)] + pub function: Option, + pub tokenizers: Option>, + pub filters: Option>, + pub comment: Option, +} + +impl DefineAnalyzerStatement { + pub(crate) async fn compute( + &self, + _ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + _doc: Option<&CursorDoc<'_>>, + ) -> Result { + // Allowed to run? + opt.is_allowed(Action::Edit, ResourceKind::Analyzer, &Base::Db)?; + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Process the statement + let key = crate::key::database::az::new(opt.ns(), opt.db(), &self.name); + run.add_ns(opt.ns(), opt.strict).await?; + run.add_db(opt.ns(), opt.db(), opt.strict).await?; + run.set(key, self).await?; + // Release the transaction + drop(run); // Do we really need this? + // Ok all good + Ok(Value::None) + } +} + +impl Display for DefineAnalyzerStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "DEFINE ANALYZER {}", self.name)?; + if let Some(ref i) = self.function { + write!(f, " FUNCTION fn::{i}")? + } + if let Some(v) = &self.tokenizers { + let tokens: Vec = v.iter().map(|f| f.to_string()).collect(); + write!(f, " TOKENIZERS {}", tokens.join(","))?; + } + if let Some(v) = &self.filters { + let tokens: Vec = v.iter().map(|f| f.to_string()).collect(); + write!(f, " FILTERS {}", tokens.join(","))?; + } + if let Some(ref v) = self.comment { + write!(f, " COMMENT {v}")? + } + Ok(()) + } +} diff --git a/core/src/sql/v2/statements/define/database.rs b/core/src/sql/v2/statements/define/database.rs new file mode 100644 index 00000000..6e13dd80 --- /dev/null +++ b/core/src/sql/v2/statements/define/database.rs @@ -0,0 +1,66 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::iam::{Action, ResourceKind}; +use crate::sql::{changefeed::ChangeFeed, Base, Ident, Strand, Value}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct DefineDatabaseStatement { + pub id: Option, + pub name: Ident, + pub comment: Option, + pub changefeed: Option, +} + +impl DefineDatabaseStatement { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + _ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + _doc: Option<&CursorDoc<'_>>, + ) -> Result { + // Allowed to run? + opt.is_allowed(Action::Edit, ResourceKind::Database, &Base::Ns)?; + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Process the statement + let key = crate::key::namespace::db::new(opt.ns(), &self.name); + let ns = run.add_ns(opt.ns(), opt.strict).await?; + // Set the id + if self.id.is_none() && ns.id.is_some() { + let mut db = self.clone(); + db.id = Some(run.get_next_db_id(ns.id.unwrap()).await?); + // Store the db + run.set(key, db).await?; + } else { + // Store the db + run.set(key, self).await?; + } + // Ok all good + Ok(Value::None) + } +} + +impl Display for DefineDatabaseStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "DEFINE DATABASE {}", self.name)?; + if let Some(ref v) = self.comment { + write!(f, " COMMENT {v}")? + } + if let Some(ref v) = self.changefeed { + write!(f, " {v}")?; + } + Ok(()) + } +} diff --git a/core/src/sql/v2/statements/define/event.rs b/core/src/sql/v2/statements/define/event.rs new file mode 100644 index 00000000..abb3213a --- /dev/null +++ b/core/src/sql/v2/statements/define/event.rs @@ -0,0 +1,64 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::iam::{Action, ResourceKind}; +use crate::sql::{Base, Ident, Strand, Value, Values}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct DefineEventStatement { + pub name: Ident, + pub what: Ident, + pub when: Value, + pub then: Values, + pub comment: Option, +} + +impl DefineEventStatement { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + _ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + _doc: Option<&CursorDoc<'_>>, + ) -> Result { + // Allowed to run? + opt.is_allowed(Action::Edit, ResourceKind::Event, &Base::Db)?; + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Process the statement + let key = crate::key::table::ev::new(opt.ns(), opt.db(), &self.what, &self.name); + run.add_ns(opt.ns(), opt.strict).await?; + run.add_db(opt.ns(), opt.db(), opt.strict).await?; + run.add_tb(opt.ns(), opt.db(), &self.what, opt.strict).await?; + run.set(key, self).await?; + // Clear the cache + let key = crate::key::table::ev::prefix(opt.ns(), opt.db(), &self.what); + run.clr(key).await?; + // Ok all good + Ok(Value::None) + } +} + +impl Display for DefineEventStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "DEFINE EVENT {} ON {} WHEN {} THEN {}", + self.name, self.what, self.when, self.then + )?; + if let Some(ref v) = self.comment { + write!(f, " COMMENT {v}")? + } + Ok(()) + } +} diff --git a/core/src/sql/v2/statements/define/field.rs b/core/src/sql/v2/statements/define/field.rs new file mode 100644 index 00000000..a41b0116 --- /dev/null +++ b/core/src/sql/v2/statements/define/field.rs @@ -0,0 +1,138 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::iam::{Action, ResourceKind}; +use crate::sql::Part; +use crate::sql::{ + fmt::is_pretty, fmt::pretty_indent, Base, Ident, Idiom, Kind, Permissions, Strand, Value, +}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Write}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 2)] +pub struct DefineFieldStatement { + pub name: Idiom, + pub what: Ident, + pub flex: bool, + pub kind: Option, + #[revision(start = 2)] + pub readonly: bool, + pub value: Option, + pub assert: Option, + pub default: Option, + pub permissions: Permissions, + pub comment: Option, +} + +impl DefineFieldStatement { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + _ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + _doc: Option<&CursorDoc<'_>>, + ) -> Result { + // Allowed to run? + opt.is_allowed(Action::Edit, ResourceKind::Field, &Base::Db)?; + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Process the statement + let fd = self.name.to_string(); + let key = crate::key::table::fd::new(opt.ns(), opt.db(), &self.what, &fd); + run.add_ns(opt.ns(), opt.strict).await?; + run.add_db(opt.ns(), opt.db(), opt.strict).await?; + run.add_tb(opt.ns(), opt.db(), &self.what, opt.strict).await?; + + // Process possible recursive_definitions. + if let Some(mut cur_kind) = self.kind.as_ref().and_then(|x| x.inner_kind()) { + let mut name = self.name.clone(); + // find existing field definitions. + let fields = run.all_tb_fields(opt.ns(), opt.db(), &self.what).await.ok(); + loop { + let new_kind = cur_kind.inner_kind(); + name.0.push(Part::All); + + let fd = name.to_string(); + let key = crate::key::table::fd::new(opt.ns(), opt.db(), &self.what, &fd); + run.add_ns(opt.ns(), opt.strict).await?; + run.add_db(opt.ns(), opt.db(), opt.strict).await?; + + // merge the new definition with possible existing definitions. + let statement = if let Some(existing) = + fields.as_ref().and_then(|x| x.iter().find(|x| x.name == name)) + { + DefineFieldStatement { + kind: Some(cur_kind), + ..existing.clone() + } + } else { + DefineFieldStatement { + name: name.clone(), + what: self.what.clone(), + flex: self.flex, + kind: Some(cur_kind), + ..Default::default() + } + }; + + run.set(key, statement).await?; + + if let Some(new_kind) = new_kind { + cur_kind = new_kind; + } else { + break; + } + } + } + + run.set(key, self).await?; + // Clear the cache + let key = crate::key::table::fd::prefix(opt.ns(), opt.db(), &self.what); + run.clr(key).await?; + // Ok all good + Ok(Value::None) + } +} + +impl Display for DefineFieldStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "DEFINE FIELD {} ON {}", self.name, self.what)?; + if self.flex { + write!(f, " FLEXIBLE")? + } + if let Some(ref v) = self.kind { + write!(f, " TYPE {v}")? + } + if let Some(ref v) = self.default { + write!(f, " DEFAULT {v}")? + } + if self.readonly { + write!(f, " READONLY")? + } + if let Some(ref v) = self.value { + write!(f, " VALUE {v}")? + } + if let Some(ref v) = self.assert { + write!(f, " ASSERT {v}")? + } + if let Some(ref v) = self.comment { + write!(f, " COMMENT {v}")? + } + let _indent = if is_pretty() { + Some(pretty_indent()) + } else { + f.write_char(' ')?; + None + }; + write!(f, "{}", self.permissions)?; + Ok(()) + } +} diff --git a/core/src/sql/v2/statements/define/function.rs b/core/src/sql/v2/statements/define/function.rs new file mode 100644 index 00000000..dd06df9b --- /dev/null +++ b/core/src/sql/v2/statements/define/function.rs @@ -0,0 +1,74 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::iam::{Action, ResourceKind}; +use crate::sql::{ + fmt::{is_pretty, pretty_indent}, + Base, Block, Ident, Kind, Permission, Strand, Value, +}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Write}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct DefineFunctionStatement { + pub name: Ident, + pub args: Vec<(Ident, Kind)>, + pub block: Block, + pub comment: Option, + pub permissions: Permission, +} + +impl DefineFunctionStatement { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + _ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + _doc: Option<&CursorDoc<'_>>, + ) -> Result { + // Allowed to run? + opt.is_allowed(Action::Edit, ResourceKind::Function, &Base::Db)?; + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Process the statement + let key = crate::key::database::fc::new(opt.ns(), opt.db(), &self.name); + run.add_ns(opt.ns(), opt.strict).await?; + run.add_db(opt.ns(), opt.db(), opt.strict).await?; + run.set(key, self).await?; + // Ok all good + Ok(Value::None) + } +} + +impl fmt::Display for DefineFunctionStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "DEFINE FUNCTION fn::{}(", self.name.0)?; + for (i, (name, kind)) in self.args.iter().enumerate() { + if i > 0 { + f.write_str(", ")?; + } + write!(f, "${name}: {kind}")?; + } + f.write_str(") ")?; + Display::fmt(&self.block, f)?; + if let Some(ref v) = self.comment { + write!(f, " COMMENT {v}")? + } + let _indent = if is_pretty() { + Some(pretty_indent()) + } else { + f.write_char(' ')?; + None + }; + write!(f, "PERMISSIONS {}", self.permissions)?; + Ok(()) + } +} diff --git a/core/src/sql/v2/statements/define/index.rs b/core/src/sql/v2/statements/define/index.rs new file mode 100644 index 00000000..4985bd0f --- /dev/null +++ b/core/src/sql/v2/statements/define/index.rs @@ -0,0 +1,82 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::iam::{Action, ResourceKind}; +use crate::sql::{statements::UpdateStatement, Base, Ident, Idioms, Index, Strand, Value, Values}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct DefineIndexStatement { + pub name: Ident, + pub what: Ident, + pub cols: Idioms, + pub index: Index, + pub comment: Option, +} + +impl DefineIndexStatement { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + ) -> Result { + // Allowed to run? + opt.is_allowed(Action::Edit, ResourceKind::Index, &Base::Db)?; + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Process the statement + let key = crate::key::table::ix::new(opt.ns(), opt.db(), &self.what, &self.name); + run.add_ns(opt.ns(), opt.strict).await?; + run.add_db(opt.ns(), opt.db(), opt.strict).await?; + run.add_tb(opt.ns(), opt.db(), &self.what, opt.strict).await?; + run.set(key, self).await?; + // Remove the index data + let key = crate::key::index::all::new(opt.ns(), opt.db(), &self.what, &self.name); + run.delp(key, u32::MAX).await?; + // Clear the cache + let key = crate::key::table::ix::prefix(opt.ns(), opt.db(), &self.what); + run.clr(key).await?; + // Release the transaction + drop(run); + // Force queries to run + let opt = &opt.new_with_force(true); + // Don't process field queries + let opt = &opt.new_with_fields(false); + // Don't process event queries + let opt = &opt.new_with_events(false); + // Don't process table queries + let opt = &opt.new_with_tables(false); + // Update the index data + let stm = UpdateStatement { + what: Values(vec![Value::Table(self.what.clone().into())]), + ..UpdateStatement::default() + }; + stm.compute(ctx, opt, txn, doc).await?; + // Ok all good + Ok(Value::None) + } +} + +impl Display for DefineIndexStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "DEFINE INDEX {} ON {} FIELDS {}", self.name, self.what, self.cols)?; + if Index::Idx != self.index { + write!(f, " {}", self.index)?; + } + if let Some(ref v) = self.comment { + write!(f, " COMMENT {v}")? + } + Ok(()) + } +} diff --git a/core/src/sql/v2/statements/define/mod.rs b/core/src/sql/v2/statements/define/mod.rs new file mode 100644 index 00000000..db2f43e7 --- /dev/null +++ b/core/src/sql/v2/statements/define/mod.rs @@ -0,0 +1,125 @@ +mod analyzer; +mod database; +mod event; +mod field; +mod function; +mod index; +mod model; +mod namespace; +mod param; +mod scope; +mod table; +mod token; +mod user; + +pub use analyzer::DefineAnalyzerStatement; +pub use database::DefineDatabaseStatement; +pub use event::DefineEventStatement; +pub use field::DefineFieldStatement; +pub use function::DefineFunctionStatement; +pub use index::DefineIndexStatement; +pub use model::DefineModelStatement; +pub use namespace::DefineNamespaceStatement; +pub use param::DefineParamStatement; +pub use scope::DefineScopeStatement; +pub use table::DefineTableStatement; +pub use token::DefineTokenStatement; +pub use user::DefineUserStatement; + +use crate::ctx::Context; +use crate::dbs::Options; +use crate::dbs::Transaction; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::value::Value; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub enum DefineStatement { + Namespace(DefineNamespaceStatement), + Database(DefineDatabaseStatement), + Function(DefineFunctionStatement), + Analyzer(DefineAnalyzerStatement), + Token(DefineTokenStatement), + Scope(DefineScopeStatement), + Param(DefineParamStatement), + Table(DefineTableStatement), + Event(DefineEventStatement), + Field(DefineFieldStatement), + Index(DefineIndexStatement), + User(DefineUserStatement), + Model(DefineModelStatement), +} + +impl DefineStatement { + /// Check if we require a writeable transaction + pub(crate) fn writeable(&self) -> bool { + true + } + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + ) -> Result { + match self { + Self::Namespace(ref v) => v.compute(ctx, opt, txn, doc).await, + Self::Database(ref v) => v.compute(ctx, opt, txn, doc).await, + Self::Function(ref v) => v.compute(ctx, opt, txn, doc).await, + Self::Token(ref v) => v.compute(ctx, opt, txn, doc).await, + Self::Scope(ref v) => v.compute(ctx, opt, txn, doc).await, + Self::Param(ref v) => v.compute(ctx, opt, txn, doc).await, + Self::Table(ref v) => v.compute(ctx, opt, txn, doc).await, + Self::Event(ref v) => v.compute(ctx, opt, txn, doc).await, + Self::Field(ref v) => v.compute(ctx, opt, txn, doc).await, + Self::Index(ref v) => v.compute(ctx, opt, txn, doc).await, + Self::Analyzer(ref v) => v.compute(ctx, opt, txn, doc).await, + Self::User(ref v) => v.compute(ctx, opt, txn, doc).await, + Self::Model(ref v) => v.compute(ctx, opt, txn, doc).await, + } + } +} + +impl Display for DefineStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Namespace(v) => Display::fmt(v, f), + Self::Database(v) => Display::fmt(v, f), + Self::Function(v) => Display::fmt(v, f), + Self::User(v) => Display::fmt(v, f), + Self::Token(v) => Display::fmt(v, f), + Self::Scope(v) => Display::fmt(v, f), + Self::Param(v) => Display::fmt(v, f), + Self::Table(v) => Display::fmt(v, f), + Self::Event(v) => Display::fmt(v, f), + Self::Field(v) => Display::fmt(v, f), + Self::Index(v) => Display::fmt(v, f), + Self::Analyzer(v) => Display::fmt(v, f), + Self::Model(v) => Display::fmt(v, f), + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::sql::Ident; + + #[test] + fn check_define_serialize() { + let stm = DefineStatement::Namespace(DefineNamespaceStatement { + name: Ident::from("test"), + ..Default::default() + }); + let enc: Vec = stm.try_into().unwrap(); + assert_eq!(11, enc.len()); + } +} diff --git a/core/src/sql/v2/statements/define/model.rs b/core/src/sql/v2/statements/define/model.rs new file mode 100644 index 00000000..8122d282 --- /dev/null +++ b/core/src/sql/v2/statements/define/model.rs @@ -0,0 +1,68 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::iam::{Action, ResourceKind}; +use crate::sql::{ + fmt::{is_pretty, pretty_indent}, + Base, Ident, Permission, Strand, Value, +}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Write}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct DefineModelStatement { + pub hash: String, + pub name: Ident, + pub version: String, + pub comment: Option, + pub permissions: Permission, +} + +impl fmt::Display for DefineModelStatement { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "DEFINE MODEL ml::{}<{}>", self.name, self.version)?; + if let Some(comment) = self.comment.as_ref() { + write!(f, " COMMENT {}", comment)?; + } + let _indent = if is_pretty() { + Some(pretty_indent()) + } else { + f.write_char(' ')?; + None + }; + write!(f, "PERMISSIONS {}", self.permissions)?; + Ok(()) + } +} + +impl DefineModelStatement { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + _ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + _doc: Option<&CursorDoc<'_>>, + ) -> Result { + // Allowed to run? + opt.is_allowed(Action::Edit, ResourceKind::Model, &Base::Db)?; + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Process the statement + let key = crate::key::database::ml::new(opt.ns(), opt.db(), &self.name, &self.version); + run.add_ns(opt.ns(), opt.strict).await?; + run.add_db(opt.ns(), opt.db(), opt.strict).await?; + run.set(key, self).await?; + // Store the model file + // TODO + // Ok all good + Ok(Value::None) + } +} diff --git a/core/src/sql/v2/statements/define/namespace.rs b/core/src/sql/v2/statements/define/namespace.rs new file mode 100644 index 00000000..e2db1a25 --- /dev/null +++ b/core/src/sql/v2/statements/define/namespace.rs @@ -0,0 +1,59 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::iam::{Action, ResourceKind}; +use crate::sql::{Base, Ident, Strand, Value}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct DefineNamespaceStatement { + pub id: Option, + pub name: Ident, + pub comment: Option, +} + +impl DefineNamespaceStatement { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + _ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + _doc: Option<&CursorDoc<'_>>, + ) -> Result { + // Allowed to run? + opt.is_allowed(Action::Edit, ResourceKind::Namespace, &Base::Root)?; + // Process the statement + let key = crate::key::root::ns::new(&self.name); + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Set the id + if self.id.is_none() { + let mut ns = self.clone(); + ns.id = Some(run.get_next_ns_id().await?); + run.set(key, ns).await?; + } else { + run.set(key, self).await?; + } + // Ok all good + Ok(Value::None) + } +} + +impl Display for DefineNamespaceStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "DEFINE NAMESPACE {}", self.name)?; + if let Some(ref v) = self.comment { + write!(f, " COMMENT {v}")? + } + Ok(()) + } +} diff --git a/core/src/sql/v2/statements/define/param.rs b/core/src/sql/v2/statements/define/param.rs new file mode 100644 index 00000000..f2be9707 --- /dev/null +++ b/core/src/sql/v2/statements/define/param.rs @@ -0,0 +1,68 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::iam::{Action, ResourceKind}; +use crate::sql::fmt::{is_pretty, pretty_indent}; +use crate::sql::{Base, Ident, Permission, Strand, Value}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Write}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct DefineParamStatement { + pub name: Ident, + pub value: Value, + pub comment: Option, + pub permissions: Permission, +} + +impl DefineParamStatement { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + ) -> Result { + // Allowed to run? + opt.is_allowed(Action::Edit, ResourceKind::Parameter, &Base::Db)?; + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Compute the param + let val = DefineParamStatement { + value: self.value.compute(ctx, opt, txn, doc).await?, + ..self.clone() + }; + // Process the statement + let key = crate::key::database::pa::new(opt.ns(), opt.db(), &self.name); + run.add_ns(opt.ns(), opt.strict).await?; + run.add_db(opt.ns(), opt.db(), opt.strict).await?; + run.set(key, val).await?; + // Ok all good + Ok(Value::None) + } +} + +impl Display for DefineParamStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "DEFINE PARAM ${} VALUE {}", self.name, self.value)?; + if let Some(ref v) = self.comment { + write!(f, " COMMENT {v}")? + } + let _indent = if is_pretty() { + Some(pretty_indent()) + } else { + f.write_char(' ')?; + None + }; + write!(f, "PERMISSIONS {}", self.permissions)?; + Ok(()) + } +} diff --git a/core/src/sql/v2/statements/define/scope.rs b/core/src/sql/v2/statements/define/scope.rs new file mode 100644 index 00000000..b4cf4888 --- /dev/null +++ b/core/src/sql/v2/statements/define/scope.rs @@ -0,0 +1,74 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::iam::{Action, ResourceKind}; +use crate::sql::{Base, Duration, Ident, Strand, Value}; +use derive::Store; +use rand::distributions::Alphanumeric; +use rand::Rng; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct DefineScopeStatement { + pub name: Ident, + pub code: String, + pub session: Option, + pub signup: Option, + pub signin: Option, + pub comment: Option, +} + +impl DefineScopeStatement { + pub(crate) fn random_code() -> String { + rand::thread_rng().sample_iter(&Alphanumeric).take(128).map(char::from).collect::() + } +} + +impl DefineScopeStatement { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + _ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + _doc: Option<&CursorDoc<'_>>, + ) -> Result { + // Allowed to run? + opt.is_allowed(Action::Edit, ResourceKind::Scope, &Base::Db)?; + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Process the statement + let key = crate::key::database::sc::new(opt.ns(), opt.db(), &self.name); + run.add_ns(opt.ns(), opt.strict).await?; + run.add_db(opt.ns(), opt.db(), opt.strict).await?; + run.set(key, self).await?; + // Ok all good + Ok(Value::None) + } +} + +impl Display for DefineScopeStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "DEFINE SCOPE {}", self.name)?; + if let Some(ref v) = self.session { + write!(f, " SESSION {v}")? + } + if let Some(ref v) = self.signup { + write!(f, " SIGNUP {v}")? + } + if let Some(ref v) = self.signin { + write!(f, " SIGNIN {v}")? + } + if let Some(ref v) = self.comment { + write!(f, " COMMENT {v}")? + } + Ok(()) + } +} diff --git a/core/src/sql/v2/statements/define/table.rs b/core/src/sql/v2/statements/define/table.rs new file mode 100644 index 00000000..f73c3f6a --- /dev/null +++ b/core/src/sql/v2/statements/define/table.rs @@ -0,0 +1,128 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::iam::{Action, ResourceKind}; +use crate::sql::{ + changefeed::ChangeFeed, + fmt::{is_pretty, pretty_indent}, + statements::UpdateStatement, + Base, Ident, Permissions, Strand, Value, Values, View, +}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Write}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct DefineTableStatement { + pub id: Option, + pub name: Ident, + pub drop: bool, + pub full: bool, + pub view: Option, + pub permissions: Permissions, + pub changefeed: Option, + pub comment: Option, +} + +impl DefineTableStatement { + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + ) -> Result { + // Allowed to run? + opt.is_allowed(Action::Edit, ResourceKind::Table, &Base::Db)?; + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Process the statement + let key = crate::key::database::tb::new(opt.ns(), opt.db(), &self.name); + let ns = run.add_ns(opt.ns(), opt.strict).await?; + let db = run.add_db(opt.ns(), opt.db(), opt.strict).await?; + let dt = if self.id.is_none() && ns.id.is_some() && db.id.is_some() { + let mut tb = self.clone(); + tb.id = Some(run.get_next_tb_id(ns.id.unwrap(), db.id.unwrap()).await?); + run.set(key, &tb).await?; + tb + } else { + run.set(key, self).await?; + self.to_owned() + }; + // Check if table is a view + if let Some(view) = &self.view { + // Remove the table data + let key = crate::key::table::all::new(opt.ns(), opt.db(), &self.name); + run.delp(key, u32::MAX).await?; + // Process each foreign table + for v in view.what.0.iter() { + // Save the view config + let key = crate::key::table::ft::new(opt.ns(), opt.db(), v, &self.name); + run.set(key, self).await?; + // Clear the cache + let key = crate::key::table::ft::prefix(opt.ns(), opt.db(), v); + run.clr(key).await?; + } + // Release the transaction + drop(run); + // Force queries to run + let opt = &opt.new_with_force(true); + // Don't process field queries + let opt = &opt.new_with_fields(false); + // Don't process event queries + let opt = &opt.new_with_events(false); + // Don't process index queries + let opt = &opt.new_with_indexes(false); + // Process each foreign table + for v in view.what.0.iter() { + // Process the view data + let stm = UpdateStatement { + what: Values(vec![Value::Table(v.clone())]), + ..UpdateStatement::default() + }; + stm.compute(ctx, opt, txn, doc).await?; + } + } else if dt.changefeed.is_some() { + run.record_table_change(opt.ns(), opt.db(), self.name.0.as_str(), &dt); + } + // Ok all good + Ok(Value::None) + } +} + +impl Display for DefineTableStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "DEFINE TABLE {}", self.name)?; + if self.drop { + f.write_str(" DROP")?; + } + f.write_str(if self.full { + " SCHEMAFULL" + } else { + " SCHEMALESS" + })?; + if let Some(ref v) = self.comment { + write!(f, " COMMENT {v}")? + } + if let Some(ref v) = self.view { + write!(f, " {v}")? + } + if let Some(ref v) = self.changefeed { + write!(f, " {v}")?; + } + let _indent = if is_pretty() { + Some(pretty_indent()) + } else { + f.write_char(' ')?; + None + }; + write!(f, "{}", self.permissions)?; + Ok(()) + } +} diff --git a/core/src/sql/v2/statements/define/token.rs b/core/src/sql/v2/statements/define/token.rs new file mode 100644 index 00000000..cd870b52 --- /dev/null +++ b/core/src/sql/v2/statements/define/token.rs @@ -0,0 +1,95 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::iam::{Action, ResourceKind}; +use crate::sql::{escape::quote_str, Algorithm, Base, Ident, Strand, Value}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct DefineTokenStatement { + pub name: Ident, + pub base: Base, + pub kind: Algorithm, + pub code: String, + pub comment: Option, +} + +impl DefineTokenStatement { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + _ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + _doc: Option<&CursorDoc<'_>>, + ) -> Result { + opt.is_allowed(Action::Edit, ResourceKind::Actor, &self.base)?; + + match &self.base { + Base::Ns => { + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Process the statement + let key = crate::key::namespace::tk::new(opt.ns(), &self.name); + run.add_ns(opt.ns(), opt.strict).await?; + run.set(key, self).await?; + // Ok all good + Ok(Value::None) + } + Base::Db => { + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Process the statement + let key = crate::key::database::tk::new(opt.ns(), opt.db(), &self.name); + run.add_ns(opt.ns(), opt.strict).await?; + run.add_db(opt.ns(), opt.db(), opt.strict).await?; + run.set(key, self).await?; + // Ok all good + Ok(Value::None) + } + Base::Sc(sc) => { + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Process the statement + let key = crate::key::scope::tk::new(opt.ns(), opt.db(), sc, &self.name); + run.add_ns(opt.ns(), opt.strict).await?; + run.add_db(opt.ns(), opt.db(), opt.strict).await?; + run.add_sc(opt.ns(), opt.db(), sc, opt.strict).await?; + run.set(key, self).await?; + // Ok all good + Ok(Value::None) + } + // Other levels are not supported + _ => Err(Error::InvalidLevel(self.base.to_string())), + } + } +} + +impl Display for DefineTokenStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "DEFINE TOKEN {} ON {} TYPE {} VALUE {}", + self.name, + self.base, + self.kind, + quote_str(&self.code) + )?; + if let Some(ref v) = self.comment { + write!(f, " COMMENT {v}")? + } + Ok(()) + } +} diff --git a/core/src/sql/v2/statements/define/user.rs b/core/src/sql/v2/statements/define/user.rs new file mode 100644 index 00000000..a466e5df --- /dev/null +++ b/core/src/sql/v2/statements/define/user.rs @@ -0,0 +1,146 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::iam::{Action, ResourceKind}; +use crate::sql::{escape::quote_str, fmt::Fmt, Base, Ident, Strand, Value}; +use argon2::{ + password_hash::{PasswordHasher, SaltString}, + Argon2, +}; +use derive::Store; +use rand::{distributions::Alphanumeric, rngs::OsRng, Rng}; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct DefineUserStatement { + pub name: Ident, + pub base: Base, + pub hash: String, + pub code: String, + pub roles: Vec, + pub comment: Option, +} + +impl From<(Base, &str, &str)> for DefineUserStatement { + fn from((base, user, pass): (Base, &str, &str)) -> Self { + DefineUserStatement { + base, + name: user.into(), + hash: Argon2::default() + .hash_password(pass.as_ref(), &SaltString::generate(&mut OsRng)) + .unwrap() + .to_string(), + code: rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(128) + .map(char::from) + .collect::(), + roles: vec!["owner".into()], + comment: None, + } + } +} + +impl DefineUserStatement { + pub(crate) fn from_parsed_values(name: Ident, base: Base, roles: Vec) -> Self { + DefineUserStatement { + name, + base, + roles, // New users get the viewer role by default + code: rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(128) + .map(char::from) + .collect::(), + ..Default::default() + } + } + + pub(crate) fn set_password(&mut self, password: &str) { + self.hash = Argon2::default() + .hash_password(password.as_bytes(), &SaltString::generate(&mut OsRng)) + .unwrap() + .to_string() + } + + pub(crate) fn set_passhash(&mut self, passhash: String) { + self.hash = passhash; + } + + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + _ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + _doc: Option<&CursorDoc<'_>>, + ) -> Result { + // Allowed to run? + opt.is_allowed(Action::Edit, ResourceKind::Actor, &self.base)?; + + match self.base { + Base::Root => { + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Process the statement + let key = crate::key::root::us::new(&self.name); + run.set(key, self).await?; + // Ok all good + Ok(Value::None) + } + Base::Ns => { + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Process the statement + let key = crate::key::namespace::us::new(opt.ns(), &self.name); + run.add_ns(opt.ns(), opt.strict).await?; + run.set(key, self).await?; + // Ok all good + Ok(Value::None) + } + Base::Db => { + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Process the statement + let key = crate::key::database::us::new(opt.ns(), opt.db(), &self.name); + run.add_ns(opt.ns(), opt.strict).await?; + run.add_db(opt.ns(), opt.db(), opt.strict).await?; + run.set(key, self).await?; + // Ok all good + Ok(Value::None) + } + // Other levels are not supported + _ => Err(Error::InvalidLevel(self.base.to_string())), + } + } +} + +impl Display for DefineUserStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "DEFINE USER {} ON {} PASSHASH {} ROLES {}", + self.name, + self.base, + quote_str(&self.hash), + Fmt::comma_separated( + &self.roles.iter().map(|r| r.to_string().to_uppercase()).collect::>() + ) + )?; + if let Some(ref v) = self.comment { + write!(f, " COMMENT {v}")? + } + Ok(()) + } +} diff --git a/core/src/sql/v2/statements/delete.rs b/core/src/sql/v2/statements/delete.rs new file mode 100644 index 00000000..90d84957 --- /dev/null +++ b/core/src/sql/v2/statements/delete.rs @@ -0,0 +1,93 @@ +use crate::ctx::Context; +use crate::dbs::{Iterator, Options, Statement, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::{Cond, Output, Timeout, Value, Values}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 2)] +pub struct DeleteStatement { + #[revision(start = 2)] + pub only: bool, + pub what: Values, + pub cond: Option, + pub output: Option, + pub timeout: Option, + pub parallel: bool, +} + +impl DeleteStatement { + /// Check if we require a writeable transaction + pub(crate) fn writeable(&self) -> bool { + true + } + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + ) -> Result { + // Valid options? + opt.valid_for_db()?; + // Create a new iterator + let mut i = Iterator::new(); + // Assign the statement + let stm = Statement::from(self); + // Ensure futures are stored + let opt = &opt.new_with_futures(false).with_projections(false); + // Loop over the delete targets + for w in self.what.0.iter() { + let v = w.compute(ctx, opt, txn, doc).await?; + i.prepare(ctx, opt, txn, &stm, v).await.map_err(|e| match e { + Error::InvalidStatementTarget { + value: v, + } => Error::DeleteStatement { + value: v, + }, + e => e, + })?; + } + // Output the results + match i.output(ctx, opt, txn, &stm).await? { + // This is a single record result + Value::Array(mut a) if self.only => match a.len() { + // There was exactly one result + 1 => Ok(a.remove(0)), + // There were no results + _ => Err(Error::SingleOnlyOutput), + }, + // This is standard query result + v => Ok(v), + } + } +} + +impl fmt::Display for DeleteStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "DELETE")?; + if self.only { + f.write_str(" ONLY")? + } + write!(f, " {}", self.what)?; + if let Some(ref v) = self.cond { + write!(f, " {v}")? + } + if let Some(ref v) = self.output { + write!(f, " {v}")? + } + if let Some(ref v) = self.timeout { + write!(f, " {v}")? + } + if self.parallel { + f.write_str(" PARALLEL")? + } + Ok(()) + } +} diff --git a/core/src/sql/v2/statements/foreach.rs b/core/src/sql/v2/statements/foreach.rs new file mode 100644 index 00000000..31d63520 --- /dev/null +++ b/core/src/sql/v2/statements/foreach.rs @@ -0,0 +1,99 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::{block::Entry, Block, Param, Value}; +use async_recursion::async_recursion; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct ForeachStatement { + pub param: Param, + pub range: Value, + pub block: Block, +} + +impl ForeachStatement { + /// Check if we require a writeable transaction + pub(crate) fn writeable(&self) -> bool { + self.range.writeable() || self.block.writeable() + } + /// Process this type returning a computed simple Value + #[cfg_attr(not(target_arch = "wasm32"), async_recursion)] + #[cfg_attr(target_arch = "wasm32", async_recursion(?Send))] + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&'async_recursion CursorDoc<'_>>, + ) -> Result { + // Check the loop data + match &self.range.compute(ctx, opt, txn, doc).await? { + Value::Array(arr) => { + // Loop over the values + 'foreach: for v in arr.iter() { + // Duplicate context + let mut ctx = Context::new(ctx); + // Set the current parameter + let key = self.param.0.to_raw(); + let val = v.compute(&ctx, opt, txn, doc).await?; + ctx.add_value(key, val); + // Loop over the code block statements + for v in self.block.iter() { + // Compute each block entry + let res = match v { + Entry::Set(v) => { + let val = v.compute(&ctx, opt, txn, doc).await?; + ctx.add_value(v.name.to_owned(), val); + Ok(Value::None) + } + Entry::Value(v) => v.compute(&ctx, opt, txn, doc).await, + Entry::Break(v) => v.compute(&ctx, opt, txn, doc).await, + Entry::Continue(v) => v.compute(&ctx, opt, txn, doc).await, + Entry::Foreach(v) => v.compute(&ctx, opt, txn, doc).await, + Entry::Ifelse(v) => v.compute(&ctx, opt, txn, doc).await, + Entry::Select(v) => v.compute(&ctx, opt, txn, doc).await, + Entry::Create(v) => v.compute(&ctx, opt, txn, doc).await, + Entry::Update(v) => v.compute(&ctx, opt, txn, doc).await, + Entry::Delete(v) => v.compute(&ctx, opt, txn, doc).await, + Entry::Relate(v) => v.compute(&ctx, opt, txn, doc).await, + Entry::Insert(v) => v.compute(&ctx, opt, txn, doc).await, + Entry::Define(v) => v.compute(&ctx, opt, txn, doc).await, + Entry::Remove(v) => v.compute(&ctx, opt, txn, doc).await, + Entry::Output(v) => { + return v.compute(&ctx, opt, txn, doc).await; + } + Entry::Throw(v) => { + return v.compute(&ctx, opt, txn, doc).await; + } + }; + // Catch any special errors + match res { + Err(Error::Continue) => continue 'foreach, + Err(Error::Break) => return Ok(Value::None), + Err(err) => return Err(err), + _ => (), + }; + } + } + // Ok all good + Ok(Value::None) + } + v => Err(Error::InvalidStatementTarget { + value: v.to_string(), + }), + } + } +} + +impl Display for ForeachStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "FOR {} IN {} {}", self.param, self.range, self.block) + } +} diff --git a/core/src/sql/v2/statements/ifelse.rs b/core/src/sql/v2/statements/ifelse.rs new file mode 100644 index 00000000..971dfc54 --- /dev/null +++ b/core/src/sql/v2/statements/ifelse.rs @@ -0,0 +1,144 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::fmt::{fmt_separated_by, is_pretty, pretty_indent, Fmt, Pretty}; +use crate::sql::Value; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Write}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct IfelseStatement { + /// The first if condition followed by a body, followed by any number of else if's + pub exprs: Vec<(Value, Value)>, + /// the final else body, if there is one + pub close: Option, +} + +impl IfelseStatement { + /// Check if we require a writeable transaction + pub(crate) fn writeable(&self) -> bool { + for (cond, then) in self.exprs.iter() { + if cond.writeable() || then.writeable() { + return true; + } + } + self.close.as_ref().map_or(false, |v| v.writeable()) + } + /// Check if we require a writeable transaction + pub(crate) fn bracketed(&self) -> bool { + self.exprs.iter().all(|(_, v)| matches!(v, Value::Block(_))) + && (self.close.as_ref().is_none() + || self.close.as_ref().is_some_and(|v| matches!(v, Value::Block(_)))) + } + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + ) -> Result { + for (ref cond, ref then) in &self.exprs { + let v = cond.compute(ctx, opt, txn, doc).await?; + if v.is_truthy() { + return then.compute(ctx, opt, txn, doc).await; + } + } + match self.close { + Some(ref v) => v.compute(ctx, opt, txn, doc).await, + None => Ok(Value::None), + } + } +} + +impl Display for IfelseStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut f = Pretty::from(f); + match self.bracketed() { + true => { + write!( + f, + "{}", + &Fmt::new( + self.exprs.iter().map(|args| { + Fmt::new(args, |(cond, then), f| { + if is_pretty() { + write!(f, "IF {cond}")?; + let indent = pretty_indent(); + write!(f, "{then}")?; + drop(indent); + } else { + write!(f, "IF {cond} {then}")?; + } + Ok(()) + }) + }), + if is_pretty() { + fmt_separated_by("ELSE") + } else { + fmt_separated_by(" ELSE ") + }, + ), + )?; + if let Some(ref v) = self.close { + if is_pretty() { + write!(f, "ELSE")?; + let indent = pretty_indent(); + write!(f, "{v}")?; + drop(indent); + } else { + write!(f, " ELSE {v}")?; + } + } + Ok(()) + } + false => { + write!( + f, + "{}", + &Fmt::new( + self.exprs.iter().map(|args| { + Fmt::new(args, |(cond, then), f| { + if is_pretty() { + write!(f, "IF {cond} THEN")?; + let indent = pretty_indent(); + write!(f, "{then}")?; + drop(indent); + } else { + write!(f, "IF {cond} THEN {then}")?; + } + Ok(()) + }) + }), + if is_pretty() { + fmt_separated_by("ELSE") + } else { + fmt_separated_by(" ELSE ") + }, + ), + )?; + if let Some(ref v) = self.close { + if is_pretty() { + write!(f, "ELSE")?; + let indent = pretty_indent(); + write!(f, "{v}")?; + drop(indent); + } else { + write!(f, " ELSE {v}")?; + } + } + if is_pretty() { + f.write_str("END")?; + } else { + f.write_str(" END")?; + } + Ok(()) + } + } + } +} diff --git a/core/src/sql/v2/statements/info.rs b/core/src/sql/v2/statements/info.rs new file mode 100644 index 00000000..27995ddc --- /dev/null +++ b/core/src/sql/v2/statements/info.rs @@ -0,0 +1,235 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::iam::Action; +use crate::iam::ResourceKind; +use crate::sql::{Base, Ident, Object, Value}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub enum InfoStatement { + Root, + Ns, + Db, + Sc(Ident), + Tb(Ident), + User(Ident, Option), +} + +impl InfoStatement { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + _ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + _doc: Option<&CursorDoc<'_>>, + ) -> Result { + // Allowed to run? + match self { + InfoStatement::Root => { + // Allowed to run? + opt.is_allowed(Action::View, ResourceKind::Any, &Base::Root)?; + // Claim transaction + let mut run = txn.lock().await; + // Create the result set + let mut res = Object::default(); + // Process the namespaces + let mut tmp = Object::default(); + for v in run.all_ns().await?.iter() { + tmp.insert(v.name.to_string(), v.to_string().into()); + } + res.insert("namespaces".to_owned(), tmp.into()); + // Process the users + let mut tmp = Object::default(); + for v in run.all_root_users().await?.iter() { + tmp.insert(v.name.to_string(), v.to_string().into()); + } + res.insert("users".to_owned(), tmp.into()); + // Ok all good + Value::from(res).ok() + } + InfoStatement::Ns => { + // Allowed to run? + opt.is_allowed(Action::View, ResourceKind::Any, &Base::Ns)?; + // Claim transaction + let mut run = txn.lock().await; + // Create the result set + let mut res = Object::default(); + // Process the databases + let mut tmp = Object::default(); + for v in run.all_db(opt.ns()).await?.iter() { + tmp.insert(v.name.to_string(), v.to_string().into()); + } + res.insert("databases".to_owned(), tmp.into()); + // Process the users + let mut tmp = Object::default(); + for v in run.all_ns_users(opt.ns()).await?.iter() { + tmp.insert(v.name.to_string(), v.to_string().into()); + } + res.insert("users".to_owned(), tmp.into()); + // Process the tokens + let mut tmp = Object::default(); + for v in run.all_ns_tokens(opt.ns()).await?.iter() { + tmp.insert(v.name.to_string(), v.to_string().into()); + } + res.insert("tokens".to_owned(), tmp.into()); + // Ok all good + Value::from(res).ok() + } + InfoStatement::Db => { + // Allowed to run? + opt.is_allowed(Action::View, ResourceKind::Any, &Base::Db)?; + // Claim transaction + let mut run = txn.lock().await; + // Create the result set + let mut res = Object::default(); + // Process the users + let mut tmp = Object::default(); + for v in run.all_db_users(opt.ns(), opt.db()).await?.iter() { + tmp.insert(v.name.to_string(), v.to_string().into()); + } + res.insert("users".to_owned(), tmp.into()); + // Process the tokens + let mut tmp = Object::default(); + for v in run.all_db_tokens(opt.ns(), opt.db()).await?.iter() { + tmp.insert(v.name.to_string(), v.to_string().into()); + } + res.insert("tokens".to_owned(), tmp.into()); + // Process the functions + let mut tmp = Object::default(); + for v in run.all_db_functions(opt.ns(), opt.db()).await?.iter() { + tmp.insert(v.name.to_string(), v.to_string().into()); + } + res.insert("functions".to_owned(), tmp.into()); + // Process the models + let mut tmp = Object::default(); + for v in run.all_db_models(opt.ns(), opt.db()).await?.iter() { + tmp.insert(format!("{}<{}>", v.name, v.version), v.to_string().into()); + } + res.insert("models".to_owned(), tmp.into()); + // Process the params + let mut tmp = Object::default(); + for v in run.all_db_params(opt.ns(), opt.db()).await?.iter() { + tmp.insert(v.name.to_string(), v.to_string().into()); + } + res.insert("params".to_owned(), tmp.into()); + // Process the scopes + let mut tmp = Object::default(); + for v in run.all_sc(opt.ns(), opt.db()).await?.iter() { + tmp.insert(v.name.to_string(), v.to_string().into()); + } + res.insert("scopes".to_owned(), tmp.into()); + // Process the tables + let mut tmp = Object::default(); + for v in run.all_tb(opt.ns(), opt.db()).await?.iter() { + tmp.insert(v.name.to_string(), v.to_string().into()); + } + res.insert("tables".to_owned(), tmp.into()); + // Process the analyzers + let mut tmp = Object::default(); + for v in run.all_db_analyzers(opt.ns(), opt.db()).await?.iter() { + tmp.insert(v.name.to_string(), v.to_string().into()); + } + res.insert("analyzers".to_owned(), tmp.into()); + // Ok all good + Value::from(res).ok() + } + InfoStatement::Sc(sc) => { + // Allowed to run? + opt.is_allowed(Action::View, ResourceKind::Any, &Base::Db)?; + // Claim transaction + let mut run = txn.lock().await; + // Create the result set + let mut res = Object::default(); + // Process the tokens + let mut tmp = Object::default(); + for v in run.all_sc_tokens(opt.ns(), opt.db(), sc).await?.iter() { + tmp.insert(v.name.to_string(), v.to_string().into()); + } + res.insert("tokens".to_owned(), tmp.into()); + // Ok all good + Value::from(res).ok() + } + InfoStatement::Tb(tb) => { + // Allowed to run? + opt.is_allowed(Action::View, ResourceKind::Any, &Base::Db)?; + // Claim transaction + let mut run = txn.lock().await; + // Create the result set + let mut res = Object::default(); + // Process the events + let mut tmp = Object::default(); + for v in run.all_tb_events(opt.ns(), opt.db(), tb).await?.iter() { + tmp.insert(v.name.to_string(), v.to_string().into()); + } + res.insert("events".to_owned(), tmp.into()); + // Process the fields + let mut tmp = Object::default(); + for v in run.all_tb_fields(opt.ns(), opt.db(), tb).await?.iter() { + tmp.insert(v.name.to_string(), v.to_string().into()); + } + res.insert("fields".to_owned(), tmp.into()); + // Process the tables + let mut tmp = Object::default(); + for v in run.all_tb_views(opt.ns(), opt.db(), tb).await?.iter() { + tmp.insert(v.name.to_string(), v.to_string().into()); + } + res.insert("tables".to_owned(), tmp.into()); + // Process the indexes + let mut tmp = Object::default(); + for v in run.all_tb_indexes(opt.ns(), opt.db(), tb).await?.iter() { + tmp.insert(v.name.to_string(), v.to_string().into()); + } + res.insert("indexes".to_owned(), tmp.into()); + // Process the live queries + let mut tmp = Object::default(); + for v in run.all_tb_lives(opt.ns(), opt.db(), tb).await?.iter() { + tmp.insert(v.id.to_raw(), v.to_string().into()); + } + res.insert("lives".to_owned(), tmp.into()); + // Ok all good + Value::from(res).ok() + } + InfoStatement::User(user, base) => { + let base = base.clone().unwrap_or(opt.selected_base()?); + // Allowed to run? + opt.is_allowed(Action::View, ResourceKind::Actor, &base)?; + + // Claim transaction + let mut run = txn.lock().await; + // Process the user + let res = match base { + Base::Root => run.get_root_user(user).await?, + Base::Ns => run.get_ns_user(opt.ns(), user).await?, + Base::Db => run.get_db_user(opt.ns(), opt.db(), user).await?, + _ => return Err(Error::InvalidLevel(base.to_string())), + }; + // Ok all good + Value::from(res.to_string()).ok() + } + } + } +} + +impl fmt::Display for InfoStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Root => f.write_str("INFO FOR ROOT"), + Self::Ns => f.write_str("INFO FOR NAMESPACE"), + Self::Db => f.write_str("INFO FOR DATABASE"), + Self::Sc(ref s) => write!(f, "INFO FOR SCOPE {s}"), + Self::Tb(ref t) => write!(f, "INFO FOR TABLE {t}"), + Self::User(ref u, ref b) => match b { + Some(ref b) => write!(f, "INFO FOR USER {u} ON {b}"), + None => write!(f, "INFO FOR USER {u}"), + }, + } + } +} diff --git a/core/src/sql/v2/statements/insert.rs b/core/src/sql/v2/statements/insert.rs new file mode 100644 index 00000000..25b047ef --- /dev/null +++ b/core/src/sql/v2/statements/insert.rs @@ -0,0 +1,123 @@ +use crate::ctx::Context; +use crate::dbs::{Iterable, Iterator, Options, Statement, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::{Data, Output, Timeout, Value}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct InsertStatement { + pub into: Value, + pub data: Data, + pub ignore: bool, + pub update: Option, + pub output: Option, + pub timeout: Option, + pub parallel: bool, +} + +impl InsertStatement { + /// Check if we require a writeable transaction + pub(crate) fn writeable(&self) -> bool { + true + } + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + ) -> Result { + // Valid options? + opt.valid_for_db()?; + // Create a new iterator + let mut i = Iterator::new(); + // Ensure futures are stored + let opt = &opt.new_with_futures(false).with_projections(false); + // Parse the expression + match self.into.compute(ctx, opt, txn, doc).await? { + Value::Table(into) => match &self.data { + // Check if this is a traditional statement + Data::ValuesExpression(v) => { + for v in v { + // Create a new empty base object + let mut o = Value::base(); + // Set each field from the expression + for (k, v) in v.iter() { + let v = v.compute(ctx, opt, txn, None).await?; + o.set(ctx, opt, txn, k, v).await?; + } + // Specify the new table record id + let id = o.rid().generate(&into, true)?; + // Pass the mergeable to the iterator + i.ingest(Iterable::Mergeable(id, o)); + } + } + // Check if this is a modern statement + Data::SingleExpression(v) => { + let v = v.compute(ctx, opt, txn, doc).await?; + match v { + Value::Array(v) => { + for v in v { + // Specify the new table record id + let id = v.rid().generate(&into, true)?; + // Pass the mergeable to the iterator + i.ingest(Iterable::Mergeable(id, v)); + } + } + Value::Object(_) => { + // Specify the new table record id + let id = v.rid().generate(&into, true)?; + // Pass the mergeable to the iterator + i.ingest(Iterable::Mergeable(id, v)); + } + v => { + return Err(Error::InsertStatement { + value: v.to_string(), + }) + } + } + } + _ => unreachable!(), + }, + v => { + return Err(Error::InsertStatement { + value: v.to_string(), + }) + } + } + // Assign the statement + let stm = Statement::from(self); + // Output the results + i.output(ctx, opt, txn, &stm).await + } +} + +impl fmt::Display for InsertStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("INSERT")?; + if self.ignore { + f.write_str(" IGNORE")? + } + write!(f, " INTO {} {}", self.into, self.data)?; + if let Some(ref v) = self.update { + write!(f, " {v}")? + } + if let Some(ref v) = self.output { + write!(f, " {v}")? + } + if let Some(ref v) = self.timeout { + write!(f, " {v}")? + } + if self.parallel { + f.write_str(" PARALLEL")? + } + Ok(()) + } +} diff --git a/core/src/sql/v2/statements/kill.rs b/core/src/sql/v2/statements/kill.rs new file mode 100644 index 00000000..3b98a0d8 --- /dev/null +++ b/core/src/sql/v2/statements/kill.rs @@ -0,0 +1,96 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::Uuid; +use crate::sql::Value; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct KillStatement { + // Uuid of Live Query + // or Param resolving to Uuid of Live Query + pub id: Value, +} + +impl KillStatement { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + _doc: Option<&CursorDoc<'_>>, + ) -> Result { + // Is realtime enabled? + opt.realtime()?; + // Valid options? + opt.valid_for_db()?; + // Resolve live query id + let live_query_id = match &self.id { + Value::Uuid(id) => *id, + Value::Param(param) => match param.compute(ctx, opt, txn, None).await? { + Value::Uuid(id) => id, + Value::Strand(id) => match uuid::Uuid::try_parse(&id) { + Ok(id) => Uuid(id), + _ => { + return Err(Error::KillStatement { + value: self.id.to_string(), + }) + } + }, + _ => { + return Err(Error::KillStatement { + value: self.id.to_string(), + }) + } + }, + _ => { + return Err(Error::KillStatement { + value: self.id.to_string(), + }) + } + }; + // Claim transaction + let mut run = txn.lock().await; + // Fetch the live query key + let key = crate::key::node::lq::new(opt.id()?, live_query_id.0, opt.ns(), opt.db()); + // Fetch the live query key if it exists + match run.get(key).await? { + Some(val) => match std::str::from_utf8(&val) { + Ok(tb) => { + // Delete the node live query + let key = + crate::key::node::lq::new(opt.id()?, live_query_id.0, opt.ns(), opt.db()); + run.del(key).await?; + // Delete the table live query + let key = crate::key::table::lq::new(opt.ns(), opt.db(), tb, live_query_id.0); + run.del(key).await?; + } + _ => { + return Err(Error::KillStatement { + value: self.id.to_string(), + }) + } + }, + None => { + return Err(Error::KillStatement { + value: self.id.to_string(), + }) + } + } + // Return the query id + Ok(Value::None) + } +} + +impl fmt::Display for KillStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "KILL {}", self.id) + } +} diff --git a/core/src/sql/v2/statements/live.rs b/core/src/sql/v2/statements/live.rs new file mode 100644 index 00000000..2850f57b --- /dev/null +++ b/core/src/sql/v2/statements/live.rs @@ -0,0 +1,137 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::iam::Auth; +use crate::sql::{Cond, Fetchs, Fields, Uuid, Value}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 2)] +pub struct LiveStatement { + pub id: Uuid, + pub node: Uuid, + pub expr: Fields, + pub what: Value, + pub cond: Option, + pub fetch: Option, + // When a live query is marked for archiving, this will + // be set to the node ID that archived the query. This + // is an internal property, set by the database runtime. + // This is optional, and os only set when archived. + pub(crate) archived: Option, + // When a live query is created, we must also store the + // authenticated session of the user who made the query, + // so we can chack it later when sending notifications. + // This is optional as it is only set by the database + // runtime when storing the live query to storage. + #[revision(start = 2)] + pub(crate) session: Option, + // When a live query is created, we must also store the + // authenticated session of the user who made the query, + // so we can chack it later when sending notifications. + // This is optional as it is only set by the database + // runtime when storing the live query to storage. + pub(crate) auth: Option, +} + +impl LiveStatement { + #[doc(hidden)] + pub fn new(expr: Fields) -> Self { + LiveStatement { + id: Uuid::new_v4(), + node: Uuid::new_v4(), + expr, + ..Default::default() + } + } + + /// Creates a live statement from parts that can be set during a query. + pub(crate) fn from_source_parts( + expr: Fields, + what: Value, + cond: Option, + fetch: Option, + ) -> Self { + LiveStatement { + id: Uuid::new_v4(), + node: Uuid::new_v4(), + expr, + what, + cond, + fetch, + ..Default::default() + } + } + + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + ) -> Result { + // Is realtime enabled? + opt.realtime()?; + // Valid options? + opt.valid_for_db()?; + // Get the Node ID + let nid = opt.id()?; + // Check that auth has been set + let mut stm = LiveStatement { + // Use the current session authentication + // for when we store the LIVE Statement + session: ctx.value("session").cloned(), + // Use the current session authentication + // for when we store the LIVE Statement + auth: Some(opt.auth.as_ref().clone()), + // Clone the rest of the original fields + // from the LIVE statement to the new one + ..self.clone() + }; + let id = stm.id.0; + // Claim transaction + let mut run = txn.lock().await; + // Process the live query table + match stm.what.compute(ctx, opt, txn, doc).await? { + Value::Table(tb) => { + // Store the current Node ID + stm.node = nid.into(); + // Insert the node live query + run.putc_ndlq(nid, id, opt.ns(), opt.db(), tb.as_str(), None).await?; + // Insert the table live query + run.putc_tblq(opt.ns(), opt.db(), &tb, stm, None).await?; + } + v => { + return Err(Error::LiveStatement { + value: v.to_string(), + }) + } + }; + // Return the query id + Ok(id.into()) + } + + pub(crate) fn archive(mut self, node_id: Uuid) -> LiveStatement { + self.archived = Some(node_id); + self + } +} + +impl fmt::Display for LiveStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "LIVE SELECT {} FROM {}", self.expr, self.what)?; + if let Some(ref v) = self.cond { + write!(f, " {v}")? + } + if let Some(ref v) = self.fetch { + write!(f, " {v}")? + } + Ok(()) + } +} diff --git a/core/src/sql/v2/statements/mod.rs b/core/src/sql/v2/statements/mod.rs new file mode 100644 index 00000000..e777c783 --- /dev/null +++ b/core/src/sql/v2/statements/mod.rs @@ -0,0 +1,65 @@ +pub(crate) mod analyze; +pub(crate) mod begin; +pub(crate) mod r#break; +pub(crate) mod cancel; +pub(crate) mod commit; +pub(crate) mod r#continue; +pub(crate) mod create; +pub(crate) mod define; +pub(crate) mod delete; +pub(crate) mod foreach; +pub(crate) mod ifelse; +pub(crate) mod info; +pub(crate) mod insert; +pub(crate) mod kill; +pub(crate) mod live; +pub(crate) mod option; +pub(crate) mod output; +pub(crate) mod relate; +pub(crate) mod remove; +pub(crate) mod select; +pub(crate) mod set; +pub(crate) mod show; +pub(crate) mod sleep; +pub(crate) mod throw; +pub(crate) mod update; +pub(crate) mod r#use; + +pub use self::analyze::AnalyzeStatement; +pub use self::begin::BeginStatement; +pub use self::cancel::CancelStatement; +pub use self::commit::CommitStatement; +pub use self::create::CreateStatement; +pub use self::delete::DeleteStatement; +pub use self::foreach::ForeachStatement; +pub use self::ifelse::IfelseStatement; +pub use self::info::InfoStatement; +pub use self::insert::InsertStatement; +pub use self::kill::KillStatement; +pub use self::live::LiveStatement; +pub use self::option::OptionStatement; +pub use self::output::OutputStatement; +pub use self::r#break::BreakStatement; +pub use self::r#continue::ContinueStatement; +pub use self::r#use::UseStatement; +pub use self::relate::RelateStatement; +pub use self::select::SelectStatement; +pub use self::set::SetStatement; +pub use self::show::ShowStatement; +pub use self::sleep::SleepStatement; +pub use self::throw::ThrowStatement; +pub use self::update::UpdateStatement; + +pub use self::define::{ + DefineAnalyzerStatement, DefineDatabaseStatement, DefineEventStatement, DefineFieldStatement, + DefineFunctionStatement, DefineIndexStatement, DefineModelStatement, DefineNamespaceStatement, + DefineParamStatement, DefineScopeStatement, DefineStatement, DefineTableStatement, + DefineTokenStatement, DefineUserStatement, +}; + +pub use self::remove::{ + RemoveAnalyzerStatement, RemoveDatabaseStatement, RemoveEventStatement, RemoveFieldStatement, + RemoveFunctionStatement, RemoveIndexStatement, RemoveModelStatement, RemoveNamespaceStatement, + RemoveParamStatement, RemoveScopeStatement, RemoveStatement, RemoveTableStatement, + RemoveTokenStatement, RemoveUserStatement, +}; diff --git a/core/src/sql/v2/statements/option.rs b/core/src/sql/v2/statements/option.rs new file mode 100644 index 00000000..c00f46cf --- /dev/null +++ b/core/src/sql/v2/statements/option.rs @@ -0,0 +1,23 @@ +use crate::sql::ident::Ident; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct OptionStatement { + pub name: Ident, + pub what: bool, +} + +impl fmt::Display for OptionStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.what { + write!(f, "OPTION {}", self.name) + } else { + write!(f, "OPTION {} = FALSE", self.name) + } + } +} diff --git a/core/src/sql/v2/statements/output.rs b/core/src/sql/v2/statements/output.rs new file mode 100644 index 00000000..507d4965 --- /dev/null +++ b/core/src/sql/v2/statements/output.rs @@ -0,0 +1,56 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::fetch::Fetchs; +use crate::sql::value::Value; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct OutputStatement { + pub what: Value, + pub fetch: Option, +} + +impl OutputStatement { + /// Check if we require a writeable transaction + pub(crate) fn writeable(&self) -> bool { + self.what.writeable() + } + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + ) -> Result { + // Ensure futures are processed + let opt = &opt.new_with_futures(true); + // Process the output value + let mut val = self.what.compute(ctx, opt, txn, doc).await?; + // Fetch any + if let Some(fetchs) = &self.fetch { + for fetch in fetchs.iter() { + val.fetch(ctx, opt, txn, fetch).await?; + } + } + // + Ok(val) + } +} + +impl fmt::Display for OutputStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "RETURN {}", self.what)?; + if let Some(ref v) = self.fetch { + write!(f, " {v}")? + } + Ok(()) + } +} diff --git a/core/src/sql/v2/statements/relate.rs b/core/src/sql/v2/statements/relate.rs new file mode 100644 index 00000000..a5d3a7e1 --- /dev/null +++ b/core/src/sql/v2/statements/relate.rs @@ -0,0 +1,196 @@ +use crate::ctx::Context; +use crate::dbs::{Iterable, Iterator, Options, Statement, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::{Data, Output, Timeout, Value}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 2)] +pub struct RelateStatement { + #[revision(start = 2)] + pub only: bool, + pub kind: Value, + pub from: Value, + pub with: Value, + pub uniq: bool, + pub data: Option, + pub output: Option, + pub timeout: Option, + pub parallel: bool, +} + +impl RelateStatement { + /// Check if we require a writeable transaction + pub(crate) fn writeable(&self) -> bool { + true + } + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + ) -> Result { + // Valid options? + opt.valid_for_db()?; + // Create a new iterator + let mut i = Iterator::new(); + // Ensure futures are stored + let opt = &opt.new_with_futures(false).with_projections(false); + // Loop over the from targets + let from = { + let mut out = Vec::new(); + match self.from.compute(ctx, opt, txn, doc).await? { + Value::Thing(v) => out.push(v), + Value::Array(v) => { + for v in v { + match v { + Value::Thing(v) => out.push(v), + Value::Object(v) => match v.rid() { + Some(v) => out.push(v), + _ => { + return Err(Error::RelateStatement { + value: v.to_string(), + }) + } + }, + v => { + return Err(Error::RelateStatement { + value: v.to_string(), + }) + } + } + } + } + Value::Object(v) => match v.rid() { + Some(v) => out.push(v), + None => { + return Err(Error::RelateStatement { + value: v.to_string(), + }) + } + }, + v => { + return Err(Error::RelateStatement { + value: v.to_string(), + }) + } + }; + // } + out + }; + // Loop over the with targets + let with = { + let mut out = Vec::new(); + match self.with.compute(ctx, opt, txn, doc).await? { + Value::Thing(v) => out.push(v), + Value::Array(v) => { + for v in v { + match v { + Value::Thing(v) => out.push(v), + Value::Object(v) => match v.rid() { + Some(v) => out.push(v), + None => { + return Err(Error::RelateStatement { + value: v.to_string(), + }) + } + }, + v => { + return Err(Error::RelateStatement { + value: v.to_string(), + }) + } + } + } + } + Value::Object(v) => match v.rid() { + Some(v) => out.push(v), + None => { + return Err(Error::RelateStatement { + value: v.to_string(), + }) + } + }, + v => { + return Err(Error::RelateStatement { + value: v.to_string(), + }) + } + }; + out + }; + // + for f in from.iter() { + for w in with.iter() { + let f = f.clone(); + let w = w.clone(); + match &self.kind { + // The relation has a specific record id + Value::Thing(id) => i.ingest(Iterable::Relatable(f, id.to_owned(), w)), + // The relation does not have a specific record id + Value::Table(tb) => match &self.data { + // There is a data clause so check for a record id + Some(data) => { + let id = match data.rid(ctx, opt, txn).await? { + Some(id) => id.generate(tb, false)?, + None => tb.generate(), + }; + i.ingest(Iterable::Relatable(f, id, w)) + } + // There is no data clause so create a record id + None => i.ingest(Iterable::Relatable(f, tb.generate(), w)), + }, + // The relation can not be any other type + _ => unreachable!(), + }; + } + } + // Assign the statement + let stm = Statement::from(self); + // Output the results + match i.output(ctx, opt, txn, &stm).await? { + // This is a single record result + Value::Array(mut a) if self.only => match a.len() { + // There was exactly one result + 1 => Ok(a.remove(0)), + // There were no results + _ => Err(Error::SingleOnlyOutput), + }, + // This is standard query result + v => Ok(v), + } + } +} + +impl fmt::Display for RelateStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "RELATE")?; + if self.only { + f.write_str(" ONLY")? + } + write!(f, " {} -> {} -> {}", self.from, self.kind, self.with)?; + if self.uniq { + f.write_str(" UNIQUE")? + } + if let Some(ref v) = self.data { + write!(f, " {v}")? + } + if let Some(ref v) = self.output { + write!(f, " {v}")? + } + if let Some(ref v) = self.timeout { + write!(f, " {v}")? + } + if self.parallel { + f.write_str(" PARALLEL")? + } + Ok(()) + } +} diff --git a/core/src/sql/v2/statements/remove/analyzer.rs b/core/src/sql/v2/statements/remove/analyzer.rs new file mode 100644 index 00000000..b32c7e15 --- /dev/null +++ b/core/src/sql/v2/statements/remove/analyzer.rs @@ -0,0 +1,44 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::err::Error; +use crate::iam::{Action, ResourceKind}; +use crate::sql::{Base, Ident, Value}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Formatter}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct RemoveAnalyzerStatement { + pub name: Ident, +} + +impl RemoveAnalyzerStatement { + pub(crate) async fn compute( + &self, + _ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + ) -> Result { + // Allowed to run? + opt.is_allowed(Action::Edit, ResourceKind::Analyzer, &Base::Db)?; + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Delete the definition + let key = crate::key::database::az::new(opt.ns(), opt.db(), &self.name); + run.del(key).await?; + // TODO Check that the analyzer is not used in any schema + // Ok all good + Ok(Value::None) + } +} + +impl Display for RemoveAnalyzerStatement { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "REMOVE ANALYZER {}", self.name) + } +} diff --git a/core/src/sql/v2/statements/remove/database.rs b/core/src/sql/v2/statements/remove/database.rs new file mode 100644 index 00000000..5dbd3e1d --- /dev/null +++ b/core/src/sql/v2/statements/remove/database.rs @@ -0,0 +1,47 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::err::Error; +use crate::iam::{Action, ResourceKind}; +use crate::sql::{Base, Ident, Value}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Formatter}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct RemoveDatabaseStatement { + pub name: Ident, +} + +impl RemoveDatabaseStatement { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + _ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + ) -> Result { + // Allowed to run? + opt.is_allowed(Action::Edit, ResourceKind::Database, &Base::Ns)?; + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Delete the definition + let key = crate::key::namespace::db::new(opt.ns(), &self.name); + run.del(key).await?; + // Delete the resource data + let key = crate::key::database::all::new(opt.ns(), &self.name); + run.delp(key, u32::MAX).await?; + // Ok all good + Ok(Value::None) + } +} + +impl Display for RemoveDatabaseStatement { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "REMOVE DATABASE {}", self.name) + } +} diff --git a/core/src/sql/v2/statements/remove/event.rs b/core/src/sql/v2/statements/remove/event.rs new file mode 100644 index 00000000..30d7d121 --- /dev/null +++ b/core/src/sql/v2/statements/remove/event.rs @@ -0,0 +1,48 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::err::Error; +use crate::iam::{Action, ResourceKind}; +use crate::sql::{Base, Ident, Value}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Formatter}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct RemoveEventStatement { + pub name: Ident, + pub what: Ident, +} + +impl RemoveEventStatement { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + _ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + ) -> Result { + // Allowed to run? + opt.is_allowed(Action::Edit, ResourceKind::Event, &Base::Db)?; + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Delete the definition + let key = crate::key::table::ev::new(opt.ns(), opt.db(), &self.what, &self.name); + run.del(key).await?; + // Clear the cache + let key = crate::key::table::ev::prefix(opt.ns(), opt.db(), &self.what); + run.clr(key).await?; + // Ok all good + Ok(Value::None) + } +} + +impl Display for RemoveEventStatement { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "REMOVE EVENT {} ON {}", self.name, self.what) + } +} diff --git a/core/src/sql/v2/statements/remove/field.rs b/core/src/sql/v2/statements/remove/field.rs new file mode 100644 index 00000000..51f3f2dc --- /dev/null +++ b/core/src/sql/v2/statements/remove/field.rs @@ -0,0 +1,49 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::err::Error; +use crate::iam::{Action, ResourceKind}; +use crate::sql::{Base, Ident, Idiom, Value}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Formatter}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct RemoveFieldStatement { + pub name: Idiom, + pub what: Ident, +} + +impl RemoveFieldStatement { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + _ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + ) -> Result { + // Allowed to run? + opt.is_allowed(Action::Edit, ResourceKind::Field, &Base::Db)?; + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Delete the definition + let fd = self.name.to_string(); + let key = crate::key::table::fd::new(opt.ns(), opt.db(), &self.what, &fd); + run.del(key).await?; + // Clear the cache + let key = crate::key::table::fd::prefix(opt.ns(), opt.db(), &self.what); + run.clr(key).await?; + // Ok all good + Ok(Value::None) + } +} + +impl Display for RemoveFieldStatement { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "REMOVE FIELD {} ON {}", self.name, self.what) + } +} diff --git a/core/src/sql/v2/statements/remove/function.rs b/core/src/sql/v2/statements/remove/function.rs new file mode 100644 index 00000000..d35037d9 --- /dev/null +++ b/core/src/sql/v2/statements/remove/function.rs @@ -0,0 +1,45 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::err::Error; +use crate::iam::{Action, ResourceKind}; +use crate::sql::{Base, Ident, Value}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct RemoveFunctionStatement { + pub name: Ident, +} + +impl RemoveFunctionStatement { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + _ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + ) -> Result { + // Allowed to run? + opt.is_allowed(Action::Edit, ResourceKind::Function, &Base::Db)?; + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Delete the definition + let key = crate::key::database::fc::new(opt.ns(), opt.db(), &self.name); + run.del(key).await?; + // Ok all good + Ok(Value::None) + } +} + +impl Display for RemoveFunctionStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // Bypass ident display since we don't want backticks arround the ident. + write!(f, "REMOVE FUNCTION fn::{}", self.name.0) + } +} diff --git a/core/src/sql/v2/statements/remove/index.rs b/core/src/sql/v2/statements/remove/index.rs new file mode 100644 index 00000000..06da30c3 --- /dev/null +++ b/core/src/sql/v2/statements/remove/index.rs @@ -0,0 +1,53 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::err::Error; +use crate::iam::{Action, ResourceKind}; +use crate::sql::{Base, Ident, Value}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Formatter}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct RemoveIndexStatement { + pub name: Ident, + pub what: Ident, +} + +impl RemoveIndexStatement { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + ) -> Result { + // Allowed to run? + opt.is_allowed(Action::Edit, ResourceKind::Index, &Base::Db)?; + // Claim transaction + let mut run = txn.lock().await; + // Clear the index store cache + ctx.get_index_stores().index_removed(opt, &mut run, &self.what, &self.name).await?; + // Clear the cache + run.clear_cache(); + // Delete the definition + let key = crate::key::table::ix::new(opt.ns(), opt.db(), &self.what, &self.name); + run.del(key).await?; + // Remove the index data + let key = crate::key::index::all::new(opt.ns(), opt.db(), &self.what, &self.name); + run.delp(key, u32::MAX).await?; + // Clear the cache + let key = crate::key::table::ix::prefix(opt.ns(), opt.db(), &self.what); + run.clr(key).await?; + // Ok all good + Ok(Value::None) + } +} + +impl Display for RemoveIndexStatement { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "REMOVE INDEX {} ON {}", self.name, self.what) + } +} diff --git a/core/src/sql/v2/statements/remove/mod.rs b/core/src/sql/v2/statements/remove/mod.rs new file mode 100644 index 00000000..601c8849 --- /dev/null +++ b/core/src/sql/v2/statements/remove/mod.rs @@ -0,0 +1,107 @@ +mod analyzer; +mod database; +mod event; +mod field; +mod function; +mod index; +mod model; +mod namespace; +mod param; +mod scope; +mod table; +mod token; +mod user; + +pub use analyzer::RemoveAnalyzerStatement; +pub use database::RemoveDatabaseStatement; +pub use event::RemoveEventStatement; +pub use field::RemoveFieldStatement; +pub use function::RemoveFunctionStatement; +pub use index::RemoveIndexStatement; +pub use model::RemoveModelStatement; +pub use namespace::RemoveNamespaceStatement; +pub use param::RemoveParamStatement; +pub use scope::RemoveScopeStatement; +pub use table::RemoveTableStatement; +pub use token::RemoveTokenStatement; +pub use user::RemoveUserStatement; + +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::Value; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Formatter}; + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub enum RemoveStatement { + Namespace(RemoveNamespaceStatement), + Database(RemoveDatabaseStatement), + Function(RemoveFunctionStatement), + Analyzer(RemoveAnalyzerStatement), + Token(RemoveTokenStatement), + Scope(RemoveScopeStatement), + Param(RemoveParamStatement), + Table(RemoveTableStatement), + Event(RemoveEventStatement), + Field(RemoveFieldStatement), + Index(RemoveIndexStatement), + User(RemoveUserStatement), + Model(RemoveModelStatement), +} + +impl RemoveStatement { + /// Check if we require a writeable transaction + pub(crate) fn writeable(&self) -> bool { + true + } + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + _doc: Option<&CursorDoc<'_>>, + ) -> Result { + match self { + Self::Namespace(ref v) => v.compute(ctx, opt, txn).await, + Self::Database(ref v) => v.compute(ctx, opt, txn).await, + Self::Function(ref v) => v.compute(ctx, opt, txn).await, + Self::Token(ref v) => v.compute(ctx, opt, txn).await, + Self::Scope(ref v) => v.compute(ctx, opt, txn).await, + Self::Param(ref v) => v.compute(ctx, opt, txn).await, + Self::Table(ref v) => v.compute(ctx, opt, txn).await, + Self::Event(ref v) => v.compute(ctx, opt, txn).await, + Self::Field(ref v) => v.compute(ctx, opt, txn).await, + Self::Index(ref v) => v.compute(ctx, opt, txn).await, + Self::Analyzer(ref v) => v.compute(ctx, opt, txn).await, + Self::User(ref v) => v.compute(ctx, opt, txn).await, + Self::Model(ref v) => v.compute(ctx, opt, txn).await, + } + } +} + +impl Display for RemoveStatement { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + Self::Namespace(v) => Display::fmt(v, f), + Self::Database(v) => Display::fmt(v, f), + Self::Function(v) => Display::fmt(v, f), + Self::Token(v) => Display::fmt(v, f), + Self::Scope(v) => Display::fmt(v, f), + Self::Param(v) => Display::fmt(v, f), + Self::Table(v) => Display::fmt(v, f), + Self::Event(v) => Display::fmt(v, f), + Self::Field(v) => Display::fmt(v, f), + Self::Index(v) => Display::fmt(v, f), + Self::Analyzer(v) => Display::fmt(v, f), + Self::User(v) => Display::fmt(v, f), + Self::Model(v) => Display::fmt(v, f), + } + } +} diff --git a/core/src/sql/v2/statements/remove/model.rs b/core/src/sql/v2/statements/remove/model.rs new file mode 100644 index 00000000..dfec396e --- /dev/null +++ b/core/src/sql/v2/statements/remove/model.rs @@ -0,0 +1,48 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::err::Error; +use crate::iam::{Action, ResourceKind}; +use crate::sql::{Base, Ident, Value}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct RemoveModelStatement { + pub name: Ident, + pub version: String, +} + +impl RemoveModelStatement { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + _ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + ) -> Result { + // Allowed to run? + opt.is_allowed(Action::Edit, ResourceKind::Model, &Base::Db)?; + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Delete the definition + let key = crate::key::database::ml::new(opt.ns(), opt.db(), &self.name, &self.version); + run.del(key).await?; + // Remove the model file + // TODO + // Ok all good + Ok(Value::None) + } +} + +impl Display for RemoveModelStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // Bypass ident display since we don't want backticks arround the ident. + write!(f, "REMOVE MODEL ml::{}<{}>", self.name.0, self.version) + } +} diff --git a/core/src/sql/v2/statements/remove/namespace.rs b/core/src/sql/v2/statements/remove/namespace.rs new file mode 100644 index 00000000..51559eb1 --- /dev/null +++ b/core/src/sql/v2/statements/remove/namespace.rs @@ -0,0 +1,48 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::err::Error; +use crate::iam::{Action, ResourceKind}; +use crate::sql::{Base, Ident, Value}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Formatter}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct RemoveNamespaceStatement { + pub name: Ident, +} + +impl RemoveNamespaceStatement { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + ) -> Result { + // Allowed to run? + opt.is_allowed(Action::Edit, ResourceKind::Namespace, &Base::Root)?; + // Claim transaction + let mut run = txn.lock().await; + ctx.get_index_stores().namespace_removed(opt, &mut run).await?; + // Clear the cache + run.clear_cache(); + // Delete the definition + let key = crate::key::root::ns::new(&self.name); + run.del(key).await?; + // Delete the resource data + let key = crate::key::namespace::all::new(&self.name); + run.delp(key, u32::MAX).await?; + // Ok all good + Ok(Value::None) + } +} + +impl Display for RemoveNamespaceStatement { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "REMOVE NAMESPACE {}", self.name) + } +} diff --git a/core/src/sql/v2/statements/remove/param.rs b/core/src/sql/v2/statements/remove/param.rs new file mode 100644 index 00000000..89c82e0b --- /dev/null +++ b/core/src/sql/v2/statements/remove/param.rs @@ -0,0 +1,44 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::err::Error; +use crate::iam::{Action, ResourceKind}; +use crate::sql::{Base, Ident, Value}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Formatter}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct RemoveParamStatement { + pub name: Ident, +} + +impl RemoveParamStatement { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + _ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + ) -> Result { + // Allowed to run? + opt.is_allowed(Action::Edit, ResourceKind::Parameter, &Base::Db)?; + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Delete the definition + let key = crate::key::database::pa::new(opt.ns(), opt.db(), &self.name); + run.del(key).await?; + // Ok all good + Ok(Value::None) + } +} + +impl Display for RemoveParamStatement { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "REMOVE PARAM {}", self.name) + } +} diff --git a/core/src/sql/v2/statements/remove/scope.rs b/core/src/sql/v2/statements/remove/scope.rs new file mode 100644 index 00000000..290ad9ea --- /dev/null +++ b/core/src/sql/v2/statements/remove/scope.rs @@ -0,0 +1,47 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::err::Error; +use crate::iam::{Action, ResourceKind}; +use crate::sql::{Base, Ident, Value}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Formatter}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct RemoveScopeStatement { + pub name: Ident, +} + +impl RemoveScopeStatement { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + _ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + ) -> Result { + // Allowed to run? + opt.is_allowed(Action::Edit, ResourceKind::Scope, &Base::Db)?; + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Delete the definition + let key = crate::key::database::sc::new(opt.ns(), opt.db(), &self.name); + run.del(key).await?; + // Remove the resource data + let key = crate::key::scope::all::new(opt.ns(), opt.db(), &self.name); + run.delp(key, u32::MAX).await?; + // Ok all good + Ok(Value::None) + } +} + +impl Display for RemoveScopeStatement { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "REMOVE SCOPE {}", self.name) + } +} diff --git a/core/src/sql/v2/statements/remove/table.rs b/core/src/sql/v2/statements/remove/table.rs new file mode 100644 index 00000000..968d571e --- /dev/null +++ b/core/src/sql/v2/statements/remove/table.rs @@ -0,0 +1,77 @@ +use crate::ctx::Context; +use crate::dbs::Options; +use crate::dbs::Transaction; +use crate::err::Error; +use crate::iam::{Action, ResourceKind}; +use crate::sql::{Base, Ident, Value}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Formatter}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[revisioned(revision = 2)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct RemoveTableStatement { + pub name: Ident, + #[revision(start = 2)] + pub if_exists: bool, +} + +impl RemoveTableStatement { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + ) -> Result { + // Allowed to run? + opt.is_allowed(Action::Edit, ResourceKind::Table, &Base::Db)?; + // Claim transaction + let mut run = txn.lock().await; + // Remove the index stores + ctx.get_index_stores().table_removed(opt, &mut run, &self.name).await?; + // Clear the cache + run.clear_cache(); + // Get the defined table + match run.get_tb(opt.ns(), opt.db(), &self.name).await { + Ok(tb) => { + // Delete the definition + let key = crate::key::database::tb::new(opt.ns(), opt.db(), &self.name); + run.del(key).await?; + // Remove the resource data + let key = crate::key::table::all::new(opt.ns(), opt.db(), &self.name); + run.delp(key, u32::MAX).await?; + // Check if this is a foreign table + if let Some(view) = &tb.view { + // Process each foreign table + for v in view.what.0.iter() { + // Save the view config + let key = crate::key::table::ft::new(opt.ns(), opt.db(), v, &self.name); + run.del(key).await?; + } + } + // Ok all good + Ok(Value::None) + } + Err(err) => { + if matches!(err, Error::TbNotFound { .. }) && self.if_exists { + Ok(Value::None) + } else { + Err(err) + } + } + } + } +} + +impl Display for RemoveTableStatement { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "REMOVE TABLE {}", self.name)?; + if self.if_exists { + write!(f, " IF EXISTS")? + } + Ok(()) + } +} diff --git a/core/src/sql/v2/statements/remove/token.rs b/core/src/sql/v2/statements/remove/token.rs new file mode 100644 index 00000000..9b689f95 --- /dev/null +++ b/core/src/sql/v2/statements/remove/token.rs @@ -0,0 +1,73 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::err::Error; +use crate::iam::{Action, ResourceKind}; +use crate::sql::{Base, Ident, Value}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Formatter}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct RemoveTokenStatement { + pub name: Ident, + pub base: Base, +} + +impl RemoveTokenStatement { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + _ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + ) -> Result { + // Allowed to run? + opt.is_allowed(Action::Edit, ResourceKind::Actor, &self.base)?; + + match &self.base { + Base::Ns => { + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Delete the definition + let key = crate::key::namespace::tk::new(opt.ns(), &self.name); + run.del(key).await?; + // Ok all good + Ok(Value::None) + } + Base::Db => { + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Delete the definition + let key = crate::key::database::tk::new(opt.ns(), opt.db(), &self.name); + run.del(key).await?; + // Ok all good + Ok(Value::None) + } + Base::Sc(sc) => { + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Delete the definition + let key = crate::key::scope::tk::new(opt.ns(), opt.db(), sc, &self.name); + run.del(key).await?; + // Ok all good + Ok(Value::None) + } + _ => Err(Error::InvalidLevel(self.base.to_string())), + } + } +} + +impl Display for RemoveTokenStatement { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "REMOVE TOKEN {} ON {}", self.name, self.base) + } +} diff --git a/core/src/sql/v2/statements/remove/user.rs b/core/src/sql/v2/statements/remove/user.rs new file mode 100644 index 00000000..5248d59f --- /dev/null +++ b/core/src/sql/v2/statements/remove/user.rs @@ -0,0 +1,73 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::err::Error; +use crate::iam::{Action, ResourceKind}; +use crate::sql::{Base, Ident, Value}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Formatter}; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct RemoveUserStatement { + pub name: Ident, + pub base: Base, +} + +impl RemoveUserStatement { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + _ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + ) -> Result { + // Allowed to run? + opt.is_allowed(Action::Edit, ResourceKind::Actor, &self.base)?; + + match self.base { + Base::Root => { + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Process the statement + let key = crate::key::root::us::new(&self.name); + run.del(key).await?; + // Ok all good + Ok(Value::None) + } + Base::Ns => { + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Delete the definition + let key = crate::key::namespace::us::new(opt.ns(), &self.name); + run.del(key).await?; + // Ok all good + Ok(Value::None) + } + Base::Db => { + // Claim transaction + let mut run = txn.lock().await; + // Clear the cache + run.clear_cache(); + // Delete the definition + let key = crate::key::database::us::new(opt.ns(), opt.db(), &self.name); + run.del(key).await?; + // Ok all good + Ok(Value::None) + } + _ => Err(Error::InvalidLevel(self.base.to_string())), + } + } +} + +impl Display for RemoveUserStatement { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "REMOVE USER {} ON {}", self.name, self.base) + } +} diff --git a/core/src/sql/v2/statements/select.rs b/core/src/sql/v2/statements/select.rs new file mode 100644 index 00000000..469207ce --- /dev/null +++ b/core/src/sql/v2/statements/select.rs @@ -0,0 +1,214 @@ +use crate::ctx::Context; +use crate::dbs::{Iterable, Iterator, Options, Statement, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::idx::planner::QueryPlanner; +use crate::sql::{ + Cond, Explain, Fetchs, Field, Fields, Groups, Idioms, Limit, Orders, Splits, Start, Timeout, + Value, Values, Version, With, +}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[revisioned(revision = 2)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct SelectStatement { + pub expr: Fields, + pub omit: Option, + #[revision(start = 2)] + pub only: bool, + pub what: Values, + pub with: Option, + pub cond: Option, + pub split: Option, + pub group: Option, + pub order: Option, + pub limit: Option, + pub start: Option, + pub fetch: Option, + pub version: Option, + pub timeout: Option, + pub parallel: bool, + pub explain: Option, +} + +impl SelectStatement { + /// Check if we require a writeable transaction + pub(crate) fn writeable(&self) -> bool { + if self.expr.iter().any(|v| match v { + Field::All => false, + Field::Single { + expr, + .. + } => expr.writeable(), + }) { + return true; + } + if self.what.iter().any(|v| v.writeable()) { + return true; + } + self.cond.as_ref().map_or(false, |v| v.writeable()) + } + + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + ) -> Result { + // Valid options? + opt.valid_for_db()?; + // Create a new iterator + let mut i = Iterator::new(); + // Ensure futures are stored + let opt = &opt.new_with_futures(false).with_projections(true); + // Get a query planner + let mut planner = QueryPlanner::new(opt, &self.with, &self.cond); + // Used for ONLY: is the limit 1? + let limit_is_one_or_zero = match &self.limit { + Some(l) => l.process(ctx, opt, txn, doc).await? <= 1, + _ => false, + }; + // Fail for multiple targets without a limit + if self.only && !limit_is_one_or_zero && self.what.0.len() > 1 { + return Err(Error::SingleOnlyOutput); + } + // Loop over the select targets + for w in self.what.0.iter() { + let v = w.compute(ctx, opt, txn, doc).await?; + match v { + Value::Table(t) => { + if self.only && !limit_is_one_or_zero { + return Err(Error::SingleOnlyOutput); + } + + planner.add_iterables(ctx, txn, t, &mut i).await?; + } + Value::Thing(v) => i.ingest(Iterable::Thing(v)), + Value::Range(v) => { + if self.only && !limit_is_one_or_zero { + return Err(Error::SingleOnlyOutput); + } + + i.ingest(Iterable::Range(*v)) + } + Value::Edges(v) => { + if self.only && !limit_is_one_or_zero { + return Err(Error::SingleOnlyOutput); + } + + i.ingest(Iterable::Edges(*v)) + } + Value::Mock(v) => { + if self.only && !limit_is_one_or_zero { + return Err(Error::SingleOnlyOutput); + } + + for v in v { + i.ingest(Iterable::Thing(v)); + } + } + Value::Array(v) => { + if self.only && !limit_is_one_or_zero { + return Err(Error::SingleOnlyOutput); + } + + for v in v { + match v { + Value::Table(t) => { + planner.add_iterables(ctx, txn, t, &mut i).await?; + } + Value::Thing(v) => i.ingest(Iterable::Thing(v)), + Value::Edges(v) => i.ingest(Iterable::Edges(*v)), + Value::Mock(v) => { + for v in v { + i.ingest(Iterable::Thing(v)); + } + } + _ => i.ingest(Iterable::Value(v)), + } + } + } + v => i.ingest(Iterable::Value(v)), + }; + } + // Create a new context + let mut ctx = Context::new(ctx); + // Assign the statement + let stm = Statement::from(self); + // Add query executors if any + if planner.has_executors() { + ctx.set_query_planner(&planner); + } + // Output the results + match i.output(&ctx, opt, txn, &stm).await? { + // This is a single record result + Value::Array(mut a) if self.only => match a.len() { + // There were no results + 0 => Ok(Value::None), + // There was exactly one result + 1 => Ok(a.remove(0)), + // There were no results + _ => Err(Error::SingleOnlyOutput), + }, + // This is standard query result + v => Ok(v), + } + } +} + +impl fmt::Display for SelectStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "SELECT {}", self.expr)?; + if let Some(ref v) = self.omit { + write!(f, " OMIT {v}")? + } + write!(f, " FROM")?; + if self.only { + f.write_str(" ONLY")? + } + write!(f, " {}", self.what)?; + if let Some(ref v) = self.with { + write!(f, " {v}")? + } + if let Some(ref v) = self.cond { + write!(f, " {v}")? + } + if let Some(ref v) = self.split { + write!(f, " {v}")? + } + if let Some(ref v) = self.group { + write!(f, " {v}")? + } + if let Some(ref v) = self.order { + write!(f, " {v}")? + } + if let Some(ref v) = self.limit { + write!(f, " {v}")? + } + if let Some(ref v) = self.start { + write!(f, " {v}")? + } + if let Some(ref v) = self.fetch { + write!(f, " {v}")? + } + if let Some(ref v) = self.version { + write!(f, " {v}")? + } + if let Some(ref v) = self.timeout { + write!(f, " {v}")? + } + if self.parallel { + f.write_str(" PARALLEL")? + } + if let Some(ref v) = self.explain { + write!(f, " {v}")? + } + Ok(()) + } +} diff --git a/core/src/sql/v2/statements/set.rs b/core/src/sql/v2/statements/set.rs new file mode 100644 index 00000000..c2b1be29 --- /dev/null +++ b/core/src/sql/v2/statements/set.rs @@ -0,0 +1,50 @@ +use crate::cnf::PROTECTED_PARAM_NAMES; +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::Value; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct SetStatement { + pub name: String, + pub what: Value, +} + +impl SetStatement { + /// Check if we require a writeable transaction + pub(crate) fn writeable(&self) -> bool { + self.what.writeable() + } + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + ) -> Result { + // Check if the variable is a protected variable + match PROTECTED_PARAM_NAMES.contains(&self.name.as_str()) { + // The variable isn't protected and can be stored + false => self.what.compute(ctx, opt, txn, doc).await, + // The user tried to set a protected variable + true => Err(Error::InvalidParam { + // Move the parameter name, as we no longer need it + name: self.name.to_owned(), + }), + } + } +} + +impl fmt::Display for SetStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "LET ${} = {}", self.name, self.what) + } +} diff --git a/core/src/sql/v2/statements/show.rs b/core/src/sql/v2/statements/show.rs new file mode 100644 index 00000000..1b176bc0 --- /dev/null +++ b/core/src/sql/v2/statements/show.rs @@ -0,0 +1,84 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::iam::{Action, ResourceKind}; +use crate::sql::{Base, Datetime, Table, Value}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub enum ShowSince { + Timestamp(Datetime), + Versionstamp(u64), +} + +// ShowStatement is used to show changes in a table or database via +// the SHOW CHANGES statement. +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct ShowStatement { + pub table: Option
, + pub since: ShowSince, + pub limit: Option, +} + +impl ShowStatement { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + _ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + _doc: Option<&CursorDoc<'_>>, + ) -> Result { + // Selected DB? + opt.is_allowed(Action::View, ResourceKind::Table, &Base::Db)?; + // Clone transaction + let txn = txn.clone(); + // Claim transaction + let mut run = txn.lock().await; + // Process the show query + let tb = self.table.as_deref(); + let r = crate::cf::read( + &mut run, + opt.ns(), + opt.db(), + tb.map(|x| x.as_str()), + self.since.clone(), + self.limit, + ) + .await?; + // Return the changes + let mut a = Vec::::new(); + for r in r.iter() { + let v: Value = r.clone().into_value(); + a.push(v); + } + let v: Value = Value::Array(crate::sql::array::Array(a)); + Ok(v) + } +} + +impl fmt::Display for ShowStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "SHOW CHANGES FOR")?; + match self.table { + Some(ref v) => write!(f, " TABLE {}", v)?, + None => write!(f, " DATABASE")?, + } + match self.since { + ShowSince::Timestamp(ref v) => write!(f, " SINCE {}", v)?, + ShowSince::Versionstamp(ref v) => write!(f, " SINCE {}", v)?, + } + if let Some(ref v) = self.limit { + write!(f, " LIMIT {}", v)? + } + Ok(()) + } +} diff --git a/core/src/sql/v2/statements/sleep.rs b/core/src/sql/v2/statements/sleep.rs new file mode 100644 index 00000000..a7085606 --- /dev/null +++ b/core/src/sql/v2/statements/sleep.rs @@ -0,0 +1,68 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::iam::{Action, ResourceKind}; +use crate::sql::{Base, Duration, Value}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[revisioned(revision = 1)] +pub struct SleepStatement { + pub(crate) duration: Duration, +} + +impl SleepStatement { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + _txn: &Transaction, + _doc: Option<&CursorDoc<'_>>, + ) -> Result { + // Allowed to run? + opt.is_allowed(Action::Edit, ResourceKind::Table, &Base::Root)?; + // Calculate the sleep duration + let dur = match (ctx.timeout(), self.duration.0) { + (Some(t), d) if t < d => t, + (_, d) => d, + }; + // Sleep for the specified time + #[cfg(target_arch = "wasm32")] + wasmtimer::tokio::sleep(dur).await; + #[cfg(not(target_arch = "wasm32"))] + tokio::time::sleep(dur).await; + // Ok all good + Ok(Value::None) + } +} + +impl fmt::Display for SleepStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "SLEEP {}", self.duration) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::dbs::test::mock; + use crate::sql::{Duration, Value}; + use std::time::{self, SystemTime}; + + #[tokio::test] + async fn test_sleep_compute() { + let time = SystemTime::now(); + let (ctx, opt, txn) = mock().await; + let stm = SleepStatement { + duration: Duration(time::Duration::from_micros(500)), + }; + let value = stm.compute(&ctx, &opt, &txn, None).await.unwrap(); + assert!(time.elapsed().unwrap() >= time::Duration::from_micros(500)); + assert_eq!(value, Value::None); + } +} diff --git a/core/src/sql/v2/statements/throw.rs b/core/src/sql/v2/statements/throw.rs new file mode 100644 index 00000000..6446b028 --- /dev/null +++ b/core/src/sql/v2/statements/throw.rs @@ -0,0 +1,39 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::Value; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct ThrowStatement { + pub error: Value, +} + +impl ThrowStatement { + /// Check if we require a writeable transaction + pub(crate) fn writeable(&self) -> bool { + false + } + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + ) -> Result { + Err(Error::Thrown(self.error.compute(ctx, opt, txn, doc).await?.to_raw_string())) + } +} + +impl fmt::Display for ThrowStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "THROW {}", self.error) + } +} diff --git a/core/src/sql/v2/statements/update.rs b/core/src/sql/v2/statements/update.rs new file mode 100644 index 00000000..56027d47 --- /dev/null +++ b/core/src/sql/v2/statements/update.rs @@ -0,0 +1,97 @@ +use crate::ctx::Context; +use crate::dbs::{Iterator, Options, Statement, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::{Cond, Data, Output, Timeout, Value, Values}; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[revisioned(revision = 2)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct UpdateStatement { + #[revision(start = 2)] + pub only: bool, + pub what: Values, + pub data: Option, + pub cond: Option, + pub output: Option, + pub timeout: Option, + pub parallel: bool, +} + +impl UpdateStatement { + /// Check if we require a writeable transaction + pub(crate) fn writeable(&self) -> bool { + true + } + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + ) -> Result { + // Valid options? + opt.valid_for_db()?; + // Create a new iterator + let mut i = Iterator::new(); + // Assign the statement + let stm = Statement::from(self); + // Ensure futures are stored + let opt = &opt.new_with_futures(false).with_projections(false); + // Loop over the update targets + for w in self.what.0.iter() { + let v = w.compute(ctx, opt, txn, doc).await?; + i.prepare(ctx, opt, txn, &stm, v).await.map_err(|e| match e { + Error::InvalidStatementTarget { + value: v, + } => Error::UpdateStatement { + value: v, + }, + e => e, + })?; + } + // Output the results + match i.output(ctx, opt, txn, &stm).await? { + // This is a single record result + Value::Array(mut a) if self.only => match a.len() { + // There was exactly one result + 1 => Ok(a.remove(0)), + // There were no results + _ => Err(Error::SingleOnlyOutput), + }, + // This is standard query result + v => Ok(v), + } + } +} + +impl fmt::Display for UpdateStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "UPDATE")?; + if self.only { + f.write_str(" ONLY")? + } + write!(f, " {}", self.what)?; + if let Some(ref v) = self.data { + write!(f, " {v}")? + } + if let Some(ref v) = self.cond { + write!(f, " {v}")? + } + if let Some(ref v) = self.output { + write!(f, " {v}")? + } + if let Some(ref v) = self.timeout { + write!(f, " {v}")? + } + if self.parallel { + f.write_str(" PARALLEL")? + } + Ok(()) + } +} diff --git a/core/src/sql/v2/statements/use.rs b/core/src/sql/v2/statements/use.rs new file mode 100644 index 00000000..399661f6 --- /dev/null +++ b/core/src/sql/v2/statements/use.rs @@ -0,0 +1,29 @@ +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; + +use crate::sql::escape::escape_ident; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct UseStatement { + pub ns: Option, + pub db: Option, +} + +impl fmt::Display for UseStatement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("USE")?; + if let Some(ref ns) = self.ns { + let ns = escape_ident(ns); + write!(f, " NS {ns}")?; + } + if let Some(ref db) = self.db { + let db = escape_ident(db); + write!(f, " DB {db}")?; + } + Ok(()) + } +} diff --git a/core/src/sql/v2/strand.rs b/core/src/sql/v2/strand.rs new file mode 100644 index 00000000..cee32486 --- /dev/null +++ b/core/src/sql/v2/strand.rs @@ -0,0 +1,147 @@ +use crate::sql::escape::quote_plain_str; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Formatter}; +use std::ops::Deref; +use std::ops::{self}; +use std::str; + +pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Strand"; + +/// A string that doesn't contain NUL bytes. +#[derive(Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash)] +#[serde(rename = "$surrealdb::private::sql::Strand")] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Strand(#[serde(with = "no_nul_bytes")] pub String); + +impl From for Strand { + fn from(s: String) -> Self { + debug_assert!(!s.contains('\0')); + Strand(s) + } +} + +impl From<&str> for Strand { + fn from(s: &str) -> Self { + debug_assert!(!s.contains('\0')); + Self::from(String::from(s)) + } +} + +impl Deref for Strand { + type Target = String; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From for String { + fn from(s: Strand) -> Self { + s.0 + } +} + +impl Strand { + /// Get the underlying String slice + pub fn as_str(&self) -> &str { + self.0.as_str() + } + /// Returns the underlying String + pub fn as_string(self) -> String { + self.0 + } + /// Convert the Strand to a raw String + pub fn to_raw(self) -> String { + self.0 + } +} + +impl Display for Strand { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + Display::fmt("e_plain_str(&self.0), f) + } +} + +impl ops::Add for Strand { + type Output = Self; + fn add(mut self, other: Self) -> Self { + self.0.push_str(other.as_str()); + self + } +} + +// serde(with = no_nul_bytes) will (de)serialize with no NUL bytes. +pub(crate) mod no_nul_bytes { + use serde::{ + de::{self, Visitor}, + Deserializer, Serializer, + }; + use std::fmt; + + pub(crate) fn serialize(s: &str, serializer: S) -> Result + where + S: Serializer, + { + debug_assert!(!s.contains('\0')); + serializer.serialize_str(s) + } + + pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct NoNulBytesVisitor; + + impl<'de> Visitor<'de> for NoNulBytesVisitor { + type Value = String; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a string without any NUL bytes") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + if value.contains('\0') { + Err(de::Error::custom("contained NUL byte")) + } else { + Ok(value.to_owned()) + } + } + + fn visit_string(self, value: String) -> Result + where + E: de::Error, + { + if value.contains('\0') { + Err(de::Error::custom("contained NUL byte")) + } else { + Ok(value) + } + } + } + + deserializer.deserialize_string(NoNulBytesVisitor) + } +} + +#[cfg(test)] +mod test { + + #[cfg(not(feature = "experimental-parser"))] + #[test] + fn ensure_strands_are_prefixed() { + use super::Strand; + + let strand = Strand("a:b".to_owned()); + assert_eq!(strand.to_string().as_str(), "s'a:b'"); + + let strand = Strand("2012-04-23T18:25:43.0000511Z".to_owned()); + assert_eq!(strand.to_string().as_str(), "s'2012-04-23T18:25:43.0000511Z'"); + + let strand = Strand("b19bc00b-aa98-486c-ae37-c8e1c54295b1".to_owned()); + assert_eq!(strand.to_string().as_str(), "s'b19bc00b-aa98-486c-ae37-c8e1c54295b1'"); + } +} diff --git a/core/src/sql/v2/subquery.rs b/core/src/sql/v2/subquery.rs new file mode 100644 index 00000000..ffd5cd18 --- /dev/null +++ b/core/src/sql/v2/subquery.rs @@ -0,0 +1,107 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::statements::{ + CreateStatement, DefineStatement, DeleteStatement, IfelseStatement, InsertStatement, + OutputStatement, RelateStatement, RemoveStatement, SelectStatement, UpdateStatement, +}; +use crate::sql::value::Value; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::cmp::Ordering; +use std::fmt::{self, Display, Formatter}; + +pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Subquery"; + +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)] +#[serde(rename = "$surrealdb::private::sql::Subquery")] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub enum Subquery { + Value(Value), + Ifelse(IfelseStatement), + Output(OutputStatement), + Select(SelectStatement), + Create(CreateStatement), + Update(UpdateStatement), + Delete(DeleteStatement), + Relate(RelateStatement), + Insert(InsertStatement), + Define(DefineStatement), + Remove(RemoveStatement), + // Add new variants here +} + +impl PartialOrd for Subquery { + #[inline] + fn partial_cmp(&self, _: &Self) -> Option { + None + } +} + +impl Subquery { + /// Check if we require a writeable transaction + pub(crate) fn writeable(&self) -> bool { + match self { + Self::Value(v) => v.writeable(), + Self::Ifelse(v) => v.writeable(), + Self::Output(v) => v.writeable(), + Self::Select(v) => v.writeable(), + Self::Create(v) => v.writeable(), + Self::Update(v) => v.writeable(), + Self::Delete(v) => v.writeable(), + Self::Relate(v) => v.writeable(), + Self::Insert(v) => v.writeable(), + Self::Define(v) => v.writeable(), + Self::Remove(v) => v.writeable(), + } + } + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + ) -> Result { + // Duplicate context + let mut ctx = Context::new(ctx); + // Add parent document + if let Some(doc) = doc { + ctx.add_value("parent", doc.doc.as_ref()); + } + // Process the subquery + match self { + Self::Value(ref v) => v.compute(&ctx, opt, txn, doc).await, + Self::Ifelse(ref v) => v.compute(&ctx, opt, txn, doc).await, + Self::Output(ref v) => v.compute(&ctx, opt, txn, doc).await, + Self::Define(ref v) => v.compute(&ctx, opt, txn, doc).await, + Self::Remove(ref v) => v.compute(&ctx, opt, txn, doc).await, + Self::Select(ref v) => v.compute(&ctx, opt, txn, doc).await, + Self::Create(ref v) => v.compute(&ctx, opt, txn, doc).await, + Self::Update(ref v) => v.compute(&ctx, opt, txn, doc).await, + Self::Delete(ref v) => v.compute(&ctx, opt, txn, doc).await, + Self::Relate(ref v) => v.compute(&ctx, opt, txn, doc).await, + Self::Insert(ref v) => v.compute(&ctx, opt, txn, doc).await, + } + } +} + +impl Display for Subquery { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + Self::Value(v) => write!(f, "({v})"), + Self::Output(v) => write!(f, "({v})"), + Self::Select(v) => write!(f, "({v})"), + Self::Create(v) => write!(f, "({v})"), + Self::Update(v) => write!(f, "({v})"), + Self::Delete(v) => write!(f, "({v})"), + Self::Relate(v) => write!(f, "({v})"), + Self::Insert(v) => write!(f, "({v})"), + Self::Define(v) => write!(f, "({v})"), + Self::Remove(v) => write!(f, "({v})"), + Self::Ifelse(v) => Display::fmt(v, f), + } + } +} diff --git a/core/src/sql/v2/table.rs b/core/src/sql/v2/table.rs new file mode 100644 index 00000000..bca5f14a --- /dev/null +++ b/core/src/sql/v2/table.rs @@ -0,0 +1,78 @@ +use crate::sql::{escape::escape_ident, fmt::Fmt, strand::no_nul_bytes, Id, Ident, Thing}; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Formatter}; +use std::ops::Deref; +use std::str; + +pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Table"; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct Tables(pub Vec
); + +impl From
for Tables { + fn from(v: Table) -> Self { + Tables(vec![v]) + } +} + +impl Deref for Tables { + type Target = Vec
; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Display for Tables { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + Display::fmt(&Fmt::comma_separated(&self.0), f) + } +} + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[serde(rename = "$surrealdb::private::sql::Table")] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub struct Table(#[serde(with = "no_nul_bytes")] pub String); + +impl From for Table { + fn from(v: String) -> Self { + Self(v) + } +} + +impl From<&str> for Table { + fn from(v: &str) -> Self { + Self::from(String::from(v)) + } +} + +impl From for Table { + fn from(v: Ident) -> Self { + Self(v.0) + } +} + +impl Deref for Table { + type Target = String; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Table { + pub fn generate(&self) -> Thing { + Thing { + tb: self.0.to_owned(), + id: Id::rand(), + } + } +} + +impl Display for Table { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + Display::fmt(&escape_ident(&self.0), f) + } +} diff --git a/core/src/sql/v2/thing.rs b/core/src/sql/v2/thing.rs new file mode 100644 index 00000000..0aca04c0 --- /dev/null +++ b/core/src/sql/v2/thing.rs @@ -0,0 +1,113 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::sql::{escape::escape_rid, id::Id, Strand, Value}; +use crate::syn; +use derive::Store; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; +use std::str::FromStr; + +pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Thing"; + +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Store, Hash)] +#[serde(rename = "$surrealdb::private::sql::Thing")] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Thing { + /// Table name + pub tb: String, + pub id: Id, +} + +impl From<(&str, Id)> for Thing { + fn from((tb, id): (&str, Id)) -> Self { + Self { + tb: tb.to_owned(), + id, + } + } +} + +impl From<(String, Id)> for Thing { + fn from((tb, id): (String, Id)) -> Self { + Self { + tb, + id, + } + } +} + +impl From<(String, String)> for Thing { + fn from((tb, id): (String, String)) -> Self { + Self::from((tb, Id::from(id))) + } +} + +impl From<(&str, &str)> for Thing { + fn from((tb, id): (&str, &str)) -> Self { + Self::from((tb.to_owned(), Id::from(id))) + } +} + +impl FromStr for Thing { + type Err = (); + fn from_str(s: &str) -> Result { + Self::try_from(s) + } +} + +impl TryFrom for Thing { + type Error = (); + fn try_from(v: String) -> Result { + Self::try_from(v.as_str()) + } +} + +impl TryFrom for Thing { + type Error = (); + fn try_from(v: Strand) -> Result { + Self::try_from(v.as_str()) + } +} + +impl TryFrom<&str> for Thing { + type Error = (); + fn try_from(v: &str) -> Result { + match syn::thing(v) { + Ok(v) => Ok(v), + _ => Err(()), + } + } +} + +impl Thing { + /// Convert the Thing to a raw String + pub fn to_raw(&self) -> String { + self.to_string() + } +} + +impl fmt::Display for Thing { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}:{}", escape_rid(&self.tb), self.id) + } +} + +impl Thing { + /// Process this type returning a computed simple Value + pub(crate) async fn compute( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&CursorDoc<'_>>, + ) -> Result { + Ok(Value::Thing(Thing { + tb: self.tb.clone(), + id: self.id.compute(ctx, opt, txn, doc).await?, + })) + } +} diff --git a/core/src/sql/v2/timeout.rs b/core/src/sql/v2/timeout.rs new file mode 100644 index 00000000..703fdcb7 --- /dev/null +++ b/core/src/sql/v2/timeout.rs @@ -0,0 +1,23 @@ +use crate::sql::duration::Duration; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; +use std::ops::Deref; + +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Timeout(pub Duration); + +impl Deref for Timeout { + type Target = Duration; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl fmt::Display for Timeout { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "TIMEOUT {}", self.0) + } +} diff --git a/core/src/sql/v2/tokenizer.rs b/core/src/sql/v2/tokenizer.rs new file mode 100644 index 00000000..2f8e05f5 --- /dev/null +++ b/core/src/sql/v2/tokenizer.rs @@ -0,0 +1,25 @@ +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt; +use std::fmt::Display; + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[revisioned(revision = 1)] +pub enum Tokenizer { + Blank, + Camel, + Class, + Punct, +} + +impl Display for Tokenizer { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(match self { + Self::Blank => "BLANK", + Self::Camel => "CAMEL", + Self::Class => "CLASS", + Self::Punct => "PUNCT", + }) + } +} diff --git a/core/src/sql/v2/uuid.rs b/core/src/sql/v2/uuid.rs new file mode 100644 index 00000000..87723272 --- /dev/null +++ b/core/src/sql/v2/uuid.rs @@ -0,0 +1,92 @@ +use crate::sql::{escape::quote_str, strand::Strand}; +use revision::revisioned; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Formatter}; +use std::ops::Deref; +use std::str; +use std::str::FromStr; + +pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Uuid"; + +#[derive( + Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize, Hash, +)] +#[serde(rename = "$surrealdb::private::sql::Uuid")] +#[revisioned(revision = 1)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Uuid(pub uuid::Uuid); + +impl From for Uuid { + fn from(v: uuid::Uuid) -> Self { + Uuid(v) + } +} + +impl From for uuid::Uuid { + fn from(s: Uuid) -> Self { + s.0 + } +} + +impl FromStr for Uuid { + type Err = (); + fn from_str(s: &str) -> Result { + Self::try_from(s) + } +} + +impl TryFrom for Uuid { + type Error = (); + fn try_from(v: String) -> Result { + Self::try_from(v.as_str()) + } +} + +impl TryFrom for Uuid { + type Error = (); + fn try_from(v: Strand) -> Result { + Self::try_from(v.as_str()) + } +} + +impl TryFrom<&str> for Uuid { + type Error = (); + fn try_from(v: &str) -> Result { + match uuid::Uuid::try_parse(v) { + Ok(v) => Ok(Self(v)), + Err(_) => Err(()), + } + } +} + +impl Deref for Uuid { + type Target = uuid::Uuid; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Uuid { + /// Generate a new UUID + pub fn new() -> Self { + Self(uuid::Uuid::now_v7()) + } + /// Generate a new V4 UUID + pub fn new_v4() -> Self { + Self(uuid::Uuid::new_v4()) + } + /// Generate a new V7 UUID + pub fn new_v7() -> Self { + Self(uuid::Uuid::now_v7()) + } + /// Convert the Uuid to a raw String + pub fn to_raw(&self) -> String { + self.0.to_string() + } +} + +impl Display for Uuid { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + Display::fmt("e_str(&self.0.to_string()), f) + } +} diff --git a/core/src/sql/v2/value/all.rs b/core/src/sql/v2/value/all.rs new file mode 100644 index 00000000..471b900c --- /dev/null +++ b/core/src/sql/v2/value/all.rs @@ -0,0 +1,8 @@ +use crate::sql::part::Part; +use crate::sql::value::Value; + +impl Value { + pub fn all(&self) -> Self { + self.pick(&[Part::All]) + } +} diff --git a/core/src/sql/v2/value/changed.rs b/core/src/sql/v2/value/changed.rs new file mode 100644 index 00000000..b7c1ab05 --- /dev/null +++ b/core/src/sql/v2/value/changed.rs @@ -0,0 +1,94 @@ +use crate::sql::idiom::Idiom; +use crate::sql::value::Value; + +impl Value { + pub(crate) fn changed(&self, val: &Value) -> Value { + match (self, val) { + (Value::Object(a), Value::Object(b)) => { + // Create an object + let mut chg = Value::base(); + // Loop over old keys + for (key, _) in a.iter() { + if !b.contains_key(key) { + let path = Idiom::from(key.clone()); + chg.put(&path, Value::None); + } + } + // Loop over new keys + for (key, val) in b.iter() { + match a.get(key) { + // Key did not exist + None => { + let path = Idiom::from(key.clone()); + chg.put(&path, val.clone()); + } + Some(old) => { + if old != val { + let path = Idiom::from(key.clone()); + chg.put(&path, old.changed(val)); + } + } + } + } + // + chg + } + (_, _) => val.clone(), + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::syn::Parse; + + #[test] + fn changed_none() { + let old = Value::parse("{ test: true, text: 'text', other: { something: true } }"); + let now = Value::parse("{ test: true, text: 'text', other: { something: true } }"); + let res = Value::parse("{}"); + assert_eq!(res, old.changed(&now)); + } + + #[test] + fn changed_add() { + let old = Value::parse("{ test: true }"); + let now = Value::parse("{ test: true, other: 'test' }"); + let res = Value::parse("{ other: 'test' }"); + assert_eq!(res, old.changed(&now)); + } + + #[test] + fn changed_remove() { + let old = Value::parse("{ test: true, other: 'test' }"); + let now = Value::parse("{ test: true }"); + let res = Value::parse("{ other: NONE }]"); + assert_eq!(res, old.changed(&now)); + } + + #[test] + fn changed_add_array() { + let old = Value::parse("{ test: [1,2,3] }"); + let now = Value::parse("{ test: [1,2,3,4] }"); + let res = Value::parse("{ test: [1,2,3,4] }"); + assert_eq!(res, old.changed(&now)); + } + + #[test] + fn changed_replace_embedded() { + let old = Value::parse("{ test: { other: 'test' } }"); + let now = Value::parse("{ test: { other: false } }"); + let res = Value::parse("{ test: { other: false } }"); + assert_eq!(res, old.changed(&now)); + } + + #[test] + fn changed_change_text() { + let old = Value::parse("{ test: { other: 'test' } }"); + let now = Value::parse("{ test: { other: 'text' } }"); + let res = Value::parse("{ test: { other: 'text' } }"); + assert_eq!(res, old.changed(&now)); + } +} diff --git a/core/src/sql/v2/value/clear.rs b/core/src/sql/v2/value/clear.rs new file mode 100644 index 00000000..bc749d2a --- /dev/null +++ b/core/src/sql/v2/value/clear.rs @@ -0,0 +1,24 @@ +use crate::err::Error; +use crate::sql::value::Value; + +impl Value { + pub fn clear(&mut self) -> Result<(), Error> { + *self = Value::None; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::syn::Parse; + + #[tokio::test] + async fn clear_value() { + let mut val = Value::parse("{ test: { other: null, something: 123 } }"); + let res = Value::None; + val.clear().unwrap(); + assert_eq!(res, val); + } +} diff --git a/core/src/sql/v2/value/compare.rs b/core/src/sql/v2/value/compare.rs new file mode 100644 index 00000000..e31c3c29 --- /dev/null +++ b/core/src/sql/v2/value/compare.rs @@ -0,0 +1,204 @@ +use crate::sql::part::Next; +use crate::sql::part::Part; +use crate::sql::value::Value; +use std::cmp::Ordering; + +impl Value { + pub(crate) fn compare( + &self, + other: &Self, + path: &[Part], + collate: bool, + numeric: bool, + ) -> Option { + match path.first() { + // Get the current path part + Some(p) => match (self, other) { + // Current path part is an object + (Value::Object(a), Value::Object(b)) => match p { + Part::Field(f) => match (a.get(f.as_str()), b.get(f.as_str())) { + (Some(a), Some(b)) => a.compare(b, path.next(), collate, numeric), + (Some(_), None) => Some(Ordering::Greater), + (None, Some(_)) => Some(Ordering::Less), + (_, _) => Some(Ordering::Equal), + }, + _ => None, + }, + // Current path part is an array + (Value::Array(a), Value::Array(b)) => match p { + Part::All => { + for (a, b) in a.iter().zip(b.iter()) { + match a.compare(b, path.next(), collate, numeric) { + Some(Ordering::Equal) => continue, + None => continue, + o => return o, + } + } + match (a.len(), b.len()) { + (a, b) if a > b => Some(Ordering::Greater), + (a, b) if a < b => Some(Ordering::Less), + _ => Some(Ordering::Equal), + } + } + Part::First => match (a.first(), b.first()) { + (Some(a), Some(b)) => a.compare(b, path.next(), collate, numeric), + (Some(_), None) => Some(Ordering::Greater), + (None, Some(_)) => Some(Ordering::Less), + (_, _) => Some(Ordering::Equal), + }, + Part::Last => match (a.last(), b.last()) { + (Some(a), Some(b)) => a.compare(b, path.next(), collate, numeric), + (Some(_), None) => Some(Ordering::Greater), + (None, Some(_)) => Some(Ordering::Less), + (_, _) => Some(Ordering::Equal), + }, + Part::Index(i) => match (a.get(i.to_usize()), b.get(i.to_usize())) { + (Some(a), Some(b)) => a.compare(b, path.next(), collate, numeric), + (Some(_), None) => Some(Ordering::Greater), + (None, Some(_)) => Some(Ordering::Less), + (_, _) => Some(Ordering::Equal), + }, + _ => { + for (a, b) in a.iter().zip(b.iter()) { + match a.compare(b, path, collate, numeric) { + Some(Ordering::Equal) => continue, + None => continue, + o => return o, + } + } + match (a.len(), b.len()) { + (a, b) if a > b => Some(Ordering::Greater), + (a, b) if a < b => Some(Ordering::Less), + _ => Some(Ordering::Equal), + } + } + }, + // Ignore everything else + (a, b) => a.compare(b, path.next(), collate, numeric), + }, + // No more parts so get the value + None => match (collate, numeric) { + (true, true) => self.natural_lexical_cmp(other), + (true, false) => self.lexical_cmp(other), + (false, true) => self.natural_cmp(other), + _ => self.partial_cmp(other), + }, + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::sql::idiom::Idiom; + use crate::syn::Parse; + + #[test] + fn compare_none() { + let idi = Idiom::default(); + let one = Value::parse("{ test: { other: null, something: 456 } }"); + let two = Value::parse("{ test: { other: null, something: 123 } }"); + let res = one.compare(&two, &idi, false, false); + assert_eq!(res, Some(Ordering::Greater)); + } + + #[test] + fn compare_basic() { + let idi = Idiom::parse("test.something"); + let one = Value::parse("{ test: { other: null, something: 456 } }"); + let two = Value::parse("{ test: { other: null, something: 123 } }"); + let res = one.compare(&two, &idi, false, false); + assert_eq!(res, Some(Ordering::Greater)); + } + + #[test] + fn compare_basic_missing_left() { + let idi = Idiom::parse("test.something"); + let one = Value::parse("{ test: { other: null } }"); + let two = Value::parse("{ test: { other: null, something: 123 } }"); + let res = one.compare(&two, &idi, false, false); + assert_eq!(res, Some(Ordering::Less)); + } + + #[test] + fn compare_basic_missing_right() { + let idi = Idiom::parse("test.something"); + let one = Value::parse("{ test: { other: null, something: 456 } }"); + let two = Value::parse("{ test: { other: null } }"); + let res = one.compare(&two, &idi, false, false); + assert_eq!(res, Some(Ordering::Greater)); + } + + #[test] + fn compare_array() { + let idi = Idiom::parse("test.something.*"); + let one = Value::parse("{ test: { other: null, something: [4, 5, 6] } }"); + let two = Value::parse("{ test: { other: null, something: [1, 2, 3] } }"); + let res = one.compare(&two, &idi, false, false); + assert_eq!(res, Some(Ordering::Greater)); + } + + #[test] + fn compare_array_longer_left() { + let idi = Idiom::parse("test.something.*"); + let one = Value::parse("{ test: { other: null, something: [1, 2, 3, 4, 5, 6] } }"); + let two = Value::parse("{ test: { other: null, something: [1, 2, 3] } }"); + let res = one.compare(&two, &idi, false, false); + assert_eq!(res, Some(Ordering::Greater)); + } + + #[test] + fn compare_array_longer_right() { + let idi = Idiom::parse("test.something.*"); + let one = Value::parse("{ test: { other: null, something: [1, 2, 3] } }"); + let two = Value::parse("{ test: { other: null, something: [1, 2, 3, 4, 5, 6] } }"); + let res = one.compare(&two, &idi, false, false); + assert_eq!(res, Some(Ordering::Less)); + } + + #[test] + fn compare_array_missing_left() { + let idi = Idiom::parse("test.something.*"); + let one = Value::parse("{ test: { other: null, something: null } }"); + let two = Value::parse("{ test: { other: null, something: [1, 2, 3] } }"); + let res = one.compare(&two, &idi, false, false); + assert_eq!(res, Some(Ordering::Less)); + } + + #[test] + fn compare_array_missing_right() { + let idi = Idiom::parse("test.something.*"); + let one = Value::parse("{ test: { other: null, something: [4, 5, 6] } }"); + let two = Value::parse("{ test: { other: null, something: null } }"); + let res = one.compare(&two, &idi, false, false); + assert_eq!(res, Some(Ordering::Greater)); + } + + #[test] + fn compare_array_missing_value_left() { + let idi = Idiom::parse("test.something.*"); + let one = Value::parse("{ test: { other: null, something: [1, null, 3] } }"); + let two = Value::parse("{ test: { other: null, something: [1, 2, 3] } }"); + let res = one.compare(&two, &idi, false, false); + assert_eq!(res, Some(Ordering::Less)); + } + + #[test] + fn compare_array_missing_value_right() { + let idi = Idiom::parse("test.something.*"); + let one = Value::parse("{ test: { other: null, something: [1, 2, 3] } }"); + let two = Value::parse("{ test: { other: null, something: [1, null, 3] } }"); + let res = one.compare(&two, &idi, false, false); + assert_eq!(res, Some(Ordering::Greater)); + } + + #[test] + fn compare_last() { + let idi = Idiom::parse("test[$]"); + let one = Value::parse("{ test: [1,5] }"); + let two = Value::parse("{ test: [2,4] }"); + let res = one.compare(&two, &idi, false, false); + assert_eq!(res, Some(Ordering::Greater)) + } +} diff --git a/core/src/sql/v2/value/cut.rs b/core/src/sql/v2/value/cut.rs new file mode 100644 index 00000000..c38a2774 --- /dev/null +++ b/core/src/sql/v2/value/cut.rs @@ -0,0 +1,188 @@ +use crate::sql::part::Next; +use crate::sql::part::Part; +use crate::sql::value::Value; + +impl Value { + /// Synchronous method for deleting a field from a `Value` + pub(crate) fn cut(&mut self, path: &[Part]) { + if let Some(p) = path.first() { + // Get the current value at path + match self { + // Current value at path is an object + Value::Object(v) => match p { + Part::Field(f) => match path.len() { + 1 => { + v.remove(f.as_str()); + } + _ => { + if let Some(v) = v.get_mut(f.as_str()) { + v.cut(path.next()) + } + } + }, + Part::Index(i) => match path.len() { + 1 => { + v.remove(&i.to_string()); + } + _ => { + if let Some(v) = v.get_mut(&i.to_string()) { + v.cut(path.next()) + } + } + }, + _ => {} + }, + // Current value at path is an array + Value::Array(v) => match p { + Part::All => match path.len() { + 1 => { + v.clear(); + } + _ => { + let path = path.next(); + v.iter_mut().for_each(|v| v.cut(path)); + } + }, + Part::First => match path.len() { + 1 => { + if !v.is_empty() { + let i = 0; + v.remove(i); + } + } + _ => { + if let Some(v) = v.first_mut() { + v.cut(path.next()) + } + } + }, + Part::Last => match path.len() { + 1 => { + if !v.is_empty() { + let i = v.len() - 1; + v.remove(i); + } + } + _ => { + if let Some(v) = v.last_mut() { + v.cut(path.next()) + } + } + }, + Part::Index(i) => match path.len() { + 1 => { + if v.len() > i.to_usize() { + v.remove(i.to_usize()); + } + } + _ => { + if let Some(v) = v.get_mut(i.to_usize()) { + v.cut(path.next()) + } + } + }, + _ => { + v.iter_mut().for_each(|v| v.cut(path)); + } + }, + // Ignore everything else + _ => (), + } + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::sql::idiom::Idiom; + use crate::syn::Parse; + + #[tokio::test] + async fn cut_none() { + let idi = Idiom::default(); + let mut val = Value::parse("{ test: { other: null, something: 123 } }"); + let res = Value::parse("{ test: { other: null, something: 123 } }"); + val.cut(&idi); + assert_eq!(res, val); + } + + #[tokio::test] + async fn cut_reset() { + let idi = Idiom::parse("test"); + let mut val = Value::parse("{ test: { other: null, something: 123 } }"); + let res = Value::parse("{ }"); + val.cut(&idi); + assert_eq!(res, val); + } + + #[tokio::test] + async fn cut_basic() { + let idi = Idiom::parse("test.something"); + let mut val = Value::parse("{ test: { other: null, something: 123 } }"); + let res = Value::parse("{ test: { other: null } }"); + val.cut(&idi); + assert_eq!(res, val); + } + + #[tokio::test] + async fn cut_wrong() { + let idi = Idiom::parse("test.something.wrong"); + let mut val = Value::parse("{ test: { other: null, something: 123 } }"); + let res = Value::parse("{ test: { other: null, something: 123 } }"); + val.cut(&idi); + assert_eq!(res, val); + } + + #[tokio::test] + async fn cut_other() { + let idi = Idiom::parse("test.other.something"); + let mut val = Value::parse("{ test: { other: null, something: 123 } }"); + let res = Value::parse("{ test: { other: null, something: 123 } }"); + val.cut(&idi); + assert_eq!(res, val); + } + + #[tokio::test] + async fn cut_array() { + let idi = Idiom::parse("test.something[1]"); + let mut val = Value::parse("{ test: { something: [123, 456, 789] } }"); + let res = Value::parse("{ test: { something: [123, 789] } }"); + val.cut(&idi); + assert_eq!(res, val); + } + + #[tokio::test] + async fn cut_array_field() { + let idi = Idiom::parse("test.something[1].age"); + let mut val = Value::parse( + "{ test: { something: [{ name: 'A', age: 34 }, { name: 'B', age: 36 }] } }", + ); + let res = Value::parse("{ test: { something: [{ name: 'A', age: 34 }, { name: 'B' }] } }"); + val.cut(&idi); + assert_eq!(res, val); + } + + #[tokio::test] + async fn cut_array_fields() { + let idi = Idiom::parse("test.something[*].age"); + let mut val = Value::parse( + "{ test: { something: [{ name: 'A', age: 34 }, { name: 'B', age: 36 }] } }", + ); + let res = Value::parse("{ test: { something: [{ name: 'A' }, { name: 'B' }] } }"); + val.cut(&idi); + assert_eq!(res, val); + } + + #[tokio::test] + async fn cut_array_fields_flat() { + let idi = Idiom::parse("test.something.age"); + let mut val = Value::parse( + "{ test: { something: [{ name: 'A', age: 34 }, { name: 'B', age: 36 }] } }", + ); + let res = Value::parse("{ test: { something: [{ name: 'A' }, { name: 'B' }] } }"); + val.cut(&idi); + assert_eq!(res, val); + } +} diff --git a/core/src/sql/v2/value/dec.rs b/core/src/sql/v2/value/dec.rs new file mode 100644 index 00000000..f1609e39 --- /dev/null +++ b/core/src/sql/v2/value/dec.rs @@ -0,0 +1,79 @@ +use crate::sql::number::Number; +use crate::sql::part::Part; +use crate::sql::value::Value; + +impl Value { + /// Synchronous method for decrementing a field in a `Value` + pub(crate) fn dec(&mut self, path: &[Part], val: Value) { + match self.pick(path) { + Value::Number(v) => { + if let Value::Number(x) = val { + self.put(path, Value::from(v - x)) + } + } + Value::Array(v) => match val { + Value::Array(x) => self.put(path, Value::from(v - x)), + x => self.put(path, Value::from(v - x)), + }, + Value::None => { + if let Value::Number(x) = val { + self.put(path, Value::from(Number::from(0) - x)) + } + } + _ => (), + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::sql::idiom::Idiom; + use crate::syn::Parse; + + #[tokio::test] + async fn decrement_none() { + let idi = Idiom::parse("other"); + let mut val = Value::parse("{ test: 100 }"); + let res = Value::parse("{ test: 100, other: -10 }"); + val.dec(&idi, Value::from(10)); + assert_eq!(res, val); + } + + #[tokio::test] + async fn decrement_number() { + let idi = Idiom::parse("test"); + let mut val = Value::parse("{ test: 100 }"); + let res = Value::parse("{ test: 90 }"); + val.dec(&idi, Value::from(10)); + assert_eq!(res, val); + } + + #[tokio::test] + async fn decrement_array_number() { + let idi = Idiom::parse("test[1]"); + let mut val = Value::parse("{ test: [100, 200, 300] }"); + let res = Value::parse("{ test: [100, 190, 300] }"); + val.dec(&idi, Value::from(10)); + assert_eq!(res, val); + } + + #[tokio::test] + async fn decrement_array_value() { + let idi = Idiom::parse("test"); + let mut val = Value::parse("{ test: [100, 200, 300] }"); + let res = Value::parse("{ test: [100, 300] }"); + val.dec(&idi, Value::from(200)); + assert_eq!(res, val); + } + + #[tokio::test] + async fn decrement_array_array() { + let idi = Idiom::parse("test"); + let mut val = Value::parse("{ test: [100, 200, 300] }"); + let res = Value::parse("{ test: [200] }"); + val.dec(&idi, Value::parse("[100, 300]")); + assert_eq!(res, val); + } +} diff --git a/core/src/sql/v2/value/decrement.rs b/core/src/sql/v2/value/decrement.rs new file mode 100644 index 00000000..a12d6efa --- /dev/null +++ b/core/src/sql/v2/value/decrement.rs @@ -0,0 +1,95 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::err::Error; +use crate::sql::number::Number; +use crate::sql::part::Part; +use crate::sql::value::Value; + +impl Value { + /// Asynchronous method for decrementing a field in a `Value` + pub(crate) async fn decrement( + &mut self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + path: &[Part], + val: Value, + ) -> Result<(), Error> { + match self.get(ctx, opt, txn, None, path).await? { + Value::Number(v) => match val { + Value::Number(x) => self.set(ctx, opt, txn, path, Value::from(v - x)).await, + _ => Ok(()), + }, + Value::Array(v) => match val { + Value::Array(x) => self.set(ctx, opt, txn, path, Value::from(v - x)).await, + x => self.set(ctx, opt, txn, path, Value::from(v - x)).await, + }, + Value::None => match val { + Value::Number(x) => { + self.set(ctx, opt, txn, path, Value::from(Number::from(0) - x)).await + } + _ => Ok(()), + }, + _ => Ok(()), + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::dbs::test::mock; + use crate::sql::idiom::Idiom; + use crate::syn::Parse; + + #[tokio::test] + async fn decrement_none() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("other"); + let mut val = Value::parse("{ test: 100 }"); + let res = Value::parse("{ test: 100, other: -10 }"); + val.decrement(&ctx, &opt, &txn, &idi, Value::from(10)).await.unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn decrement_number() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test"); + let mut val = Value::parse("{ test: 100 }"); + let res = Value::parse("{ test: 90 }"); + val.decrement(&ctx, &opt, &txn, &idi, Value::from(10)).await.unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn decrement_array_number() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test[1]"); + let mut val = Value::parse("{ test: [100, 200, 300] }"); + let res = Value::parse("{ test: [100, 190, 300] }"); + val.decrement(&ctx, &opt, &txn, &idi, Value::from(10)).await.unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn decrement_array_value() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test"); + let mut val = Value::parse("{ test: [100, 200, 300] }"); + let res = Value::parse("{ test: [100, 300] }"); + val.decrement(&ctx, &opt, &txn, &idi, Value::from(200)).await.unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn decrement_array_array() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test"); + let mut val = Value::parse("{ test: [100, 200, 300] }"); + let res = Value::parse("{ test: [200] }"); + val.decrement(&ctx, &opt, &txn, &idi, Value::parse("[100, 300]")).await.unwrap(); + assert_eq!(res, val); + } +} diff --git a/core/src/sql/v2/value/def.rs b/core/src/sql/v2/value/def.rs new file mode 100644 index 00000000..c98c6c99 --- /dev/null +++ b/core/src/sql/v2/value/def.rs @@ -0,0 +1,9 @@ +use crate::sql::paths::ID; +use crate::sql::thing::Thing; +use crate::sql::value::Value; + +impl Value { + pub(crate) fn def(&mut self, val: &Thing) { + self.put(ID.as_ref(), val.clone().into()) + } +} diff --git a/core/src/sql/v2/value/del.rs b/core/src/sql/v2/value/del.rs new file mode 100644 index 00000000..9de1eabc --- /dev/null +++ b/core/src/sql/v2/value/del.rs @@ -0,0 +1,337 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::err::Error; +use crate::exe::try_join_all_buffered; +use crate::sql::array::Abolish; +use crate::sql::part::Next; +use crate::sql::part::Part; +use crate::sql::value::Value; +use async_recursion::async_recursion; +use std::collections::HashSet; + +impl Value { + /// Asynchronous method for deleting a field from a `Value` + #[cfg_attr(not(target_arch = "wasm32"), async_recursion)] + #[cfg_attr(target_arch = "wasm32", async_recursion(?Send))] + pub(crate) async fn del( + &mut self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + path: &[Part], + ) -> Result<(), Error> { + match path.first() { + // Get the current value at path + Some(p) => match self { + // Current value at path is an object + Value::Object(v) => match p { + Part::Field(f) => match path.len() { + 1 => { + v.remove(f.as_str()); + Ok(()) + } + _ => match v.get_mut(f.as_str()) { + Some(v) if v.is_some() => v.del(ctx, opt, txn, path.next()).await, + _ => Ok(()), + }, + }, + Part::Index(i) => match path.len() { + 1 => { + v.remove(&i.to_string()); + Ok(()) + } + _ => match v.get_mut(&i.to_string()) { + Some(v) if v.is_some() => v.del(ctx, opt, txn, path.next()).await, + _ => Ok(()), + }, + }, + Part::Value(x) => match x.compute(ctx, opt, txn, None).await? { + Value::Strand(f) => match path.len() { + 1 => { + v.remove(f.as_str()); + Ok(()) + } + _ => match v.get_mut(f.as_str()) { + Some(v) if v.is_some() => v.del(ctx, opt, txn, path.next()).await, + _ => Ok(()), + }, + }, + _ => Ok(()), + }, + _ => Ok(()), + }, + // Current value at path is an array + Value::Array(v) => match p { + Part::All => match path.len() { + 1 => { + v.clear(); + Ok(()) + } + _ => { + let path = path.next(); + let futs = v.iter_mut().map(|v| v.del(ctx, opt, txn, path)); + try_join_all_buffered(futs).await?; + Ok(()) + } + }, + Part::First => match path.len() { + 1 => { + if !v.is_empty() { + let i = 0; + v.remove(i); + } + Ok(()) + } + _ => match v.first_mut() { + Some(v) => v.del(ctx, opt, txn, path.next()).await, + None => Ok(()), + }, + }, + Part::Last => match path.len() { + 1 => { + if !v.is_empty() { + let i = v.len() - 1; + v.remove(i); + } + Ok(()) + } + _ => match v.last_mut() { + Some(v) => v.del(ctx, opt, txn, path.next()).await, + None => Ok(()), + }, + }, + Part::Index(i) => match path.len() { + 1 => { + if v.len() > i.to_usize() { + v.remove(i.to_usize()); + } + Ok(()) + } + _ => match v.get_mut(i.to_usize()) { + Some(v) => v.del(ctx, opt, txn, path.next()).await, + None => Ok(()), + }, + }, + Part::Where(w) => match path.len() { + 1 => { + // TODO: If further optimization is desired, push indices to a vec, + // iterate in reverse, and call swap_remove + let mut m = HashSet::new(); + for (i, v) in v.iter().enumerate() { + let cur = v.into(); + if w.compute(ctx, opt, txn, Some(&cur)).await?.is_truthy() { + m.insert(i); + }; + } + v.abolish(|i| m.contains(&i)); + Ok(()) + } + _ => match path.next().first() { + Some(Part::Index(_)) => { + let mut a = Vec::new(); + let mut p = Vec::new(); + // Store the elements and positions to update + for (i, o) in v.iter_mut().enumerate() { + let cur = o.into(); + if w.compute(ctx, opt, txn, Some(&cur)).await?.is_truthy() { + a.push(o.clone()); + p.push(i); + } + } + // Convert the matched elements array to a value + let mut a = Value::from(a); + // Set the new value on the matches elements + a.del(ctx, opt, txn, path.next()).await?; + // Push the new values into the original array + for (i, p) in p.into_iter().enumerate().rev() { + match a.pick(&[Part::Index(i.into())]) { + Value::None => { + v.remove(i); + } + x => v[p] = x, + } + } + Ok(()) + } + _ => { + let path = path.next(); + for v in v.iter_mut() { + let cur = v.into(); + if w.compute(ctx, opt, txn, Some(&cur)).await?.is_truthy() { + v.del(ctx, opt, txn, path).await?; + } + } + Ok(()) + } + }, + }, + Part::Value(x) => match x.compute(ctx, opt, txn, None).await? { + Value::Number(i) => match path.len() { + 1 => { + if v.len() > i.to_usize() { + v.remove(i.to_usize()); + } + Ok(()) + } + _ => match v.get_mut(i.to_usize()) { + Some(v) => v.del(ctx, opt, txn, path.next()).await, + None => Ok(()), + }, + }, + _ => Ok(()), + }, + _ => { + let futs = v.iter_mut().map(|v| v.del(ctx, opt, txn, path)); + try_join_all_buffered(futs).await?; + Ok(()) + } + }, + // Ignore everything else + _ => Ok(()), + }, + // We are done + None => Ok(()), + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::dbs::test::mock; + use crate::sql::idiom::Idiom; + use crate::syn::Parse; + + #[tokio::test] + async fn del_none() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::default(); + let mut val = Value::parse("{ test: { other: null, something: 123 } }"); + let res = Value::parse("{ test: { other: null, something: 123 } }"); + val.del(&ctx, &opt, &txn, &idi).await.unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn del_reset() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test"); + let mut val = Value::parse("{ test: { other: null, something: 123 } }"); + let res = Value::parse("{ }"); + val.del(&ctx, &opt, &txn, &idi).await.unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn del_basic() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test.something"); + let mut val = Value::parse("{ test: { other: null, something: 123 } }"); + let res = Value::parse("{ test: { other: null } }"); + val.del(&ctx, &opt, &txn, &idi).await.unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn del_wrong() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test.something.wrong"); + let mut val = Value::parse("{ test: { other: null, something: 123 } }"); + let res = Value::parse("{ test: { other: null, something: 123 } }"); + val.del(&ctx, &opt, &txn, &idi).await.unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn del_other() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test.other.something"); + let mut val = Value::parse("{ test: { other: null, something: 123 } }"); + let res = Value::parse("{ test: { other: null, something: 123 } }"); + val.del(&ctx, &opt, &txn, &idi).await.unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn del_array() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test.something[1]"); + let mut val = Value::parse("{ test: { something: [123, 456, 789] } }"); + let res = Value::parse("{ test: { something: [123, 789] } }"); + val.del(&ctx, &opt, &txn, &idi).await.unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn del_array_field() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test.something[1].age"); + let mut val = Value::parse( + "{ test: { something: [{ name: 'A', age: 34 }, { name: 'B', age: 36 }] } }", + ); + let res = Value::parse("{ test: { something: [{ name: 'A', age: 34 }, { name: 'B' }] } }"); + val.del(&ctx, &opt, &txn, &idi).await.unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn del_array_fields() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test.something[*].age"); + let mut val = Value::parse( + "{ test: { something: [{ name: 'A', age: 34 }, { name: 'B', age: 36 }] } }", + ); + let res = Value::parse("{ test: { something: [{ name: 'A' }, { name: 'B' }] } }"); + val.del(&ctx, &opt, &txn, &idi).await.unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn del_array_fields_flat() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test.something.age"); + let mut val = Value::parse( + "{ test: { something: [{ name: 'A', age: 34 }, { name: 'B', age: 36 }] } }", + ); + let res = Value::parse("{ test: { something: [{ name: 'A' }, { name: 'B' }] } }"); + val.del(&ctx, &opt, &txn, &idi).await.unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn del_array_where_field() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test.something[WHERE age > 35].age"); + let mut val = Value::parse( + "{ test: { something: [{ name: 'A', age: 34 }, { name: 'B', age: 36 }] } }", + ); + let res = Value::parse("{ test: { something: [{ name: 'A', age: 34 }, { name: 'B' }] } }"); + val.del(&ctx, &opt, &txn, &idi).await.unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn del_array_where_fields() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test.something[WHERE age > 35]"); + let mut val = Value::parse( + "{ test: { something: [{ name: 'A', age: 34 }, { name: 'B', age: 36 }] } }", + ); + let res = Value::parse("{ test: { something: [{ name: 'A', age: 34 }] } }"); + val.del(&ctx, &opt, &txn, &idi).await.unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn del_array_where_fields_array_index() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test.something[WHERE age > 30][0]"); + let mut val = Value::parse( + "{ test: { something: [{ name: 'A', age: 34 }, { name: 'B', age: 36 }] } }", + ); + let res = Value::parse("{ test: { something: [{ name: 'B', age: 36 }] } }"); + val.del(&ctx, &opt, &txn, &idi).await.unwrap(); + assert_eq!(res, val); + } +} diff --git a/core/src/sql/v2/value/diff.rs b/core/src/sql/v2/value/diff.rs new file mode 100644 index 00000000..ac66f5db --- /dev/null +++ b/core/src/sql/v2/value/diff.rs @@ -0,0 +1,132 @@ +use crate::sql::idiom::Idiom; +use crate::sql::operation::Operation; +use crate::sql::value::Value; +use std::cmp::min; + +impl Value { + pub(crate) fn diff(&self, val: &Value, path: Idiom) -> Vec { + let mut ops: Vec = vec![]; + match (self, val) { + (Value::Object(a), Value::Object(b)) if a != b => { + // Loop over old keys + for (key, _) in a.iter() { + if !b.contains_key(key) { + ops.push(Operation::Remove { + path: path.clone().push(key.clone().into()), + }) + } + } + // Loop over new keys + for (key, val) in b.iter() { + match a.get(key) { + None => ops.push(Operation::Add { + path: path.clone().push(key.clone().into()), + value: val.clone(), + }), + Some(old) => { + let path = path.clone().push(key.clone().into()); + ops.append(&mut old.diff(val, path)) + } + } + } + } + (Value::Array(a), Value::Array(b)) if a != b => { + let mut n = 0; + while n < min(a.len(), b.len()) { + let path = path.clone().push(n.into()); + ops.append(&mut a[n].diff(&b[n], path)); + n += 1; + } + while n < b.len() { + if n >= a.len() { + ops.push(Operation::Add { + path: path.clone().push(n.into()), + value: b[n].clone(), + }) + } + n += 1; + } + while n < a.len() { + if n >= b.len() { + ops.push(Operation::Remove { + path: path.clone().push(n.into()), + }) + } + n += 1; + } + } + (Value::Strand(a), Value::Strand(b)) if a != b => ops.push(Operation::Change { + path, + value: { + let dmp = dmp::new(); + let pch = dmp.patch_make1(a, b); + let txt = dmp.patch_to_text(&pch); + txt.into() + }, + }), + (a, b) if a != b => ops.push(Operation::Replace { + path, + value: val.clone(), + }), + (_, _) => (), + } + ops + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::syn::Parse; + + #[test] + fn diff_none() { + let old = Value::parse("{ test: true, text: 'text', other: { something: true } }"); + let now = Value::parse("{ test: true, text: 'text', other: { something: true } }"); + let res = Value::parse("[]"); + assert_eq!(res.to_operations().unwrap(), old.diff(&now, Idiom::default())); + } + + #[test] + fn diff_add() { + let old = Value::parse("{ test: true }"); + let now = Value::parse("{ test: true, other: 'test' }"); + let res = Value::parse("[{ op: 'add', path: '/other', value: 'test' }]"); + assert_eq!(res.to_operations().unwrap(), old.diff(&now, Idiom::default())); + } + + #[test] + fn diff_remove() { + let old = Value::parse("{ test: true, other: 'test' }"); + let now = Value::parse("{ test: true }"); + let res = Value::parse("[{ op: 'remove', path: '/other' }]"); + assert_eq!(res.to_operations().unwrap(), old.diff(&now, Idiom::default())); + } + + #[test] + fn diff_add_array() { + let old = Value::parse("{ test: [1,2,3] }"); + let now = Value::parse("{ test: [1,2,3,4] }"); + let res = Value::parse("[{ op: 'add', path: '/test/3', value: 4 }]"); + assert_eq!(res.to_operations().unwrap(), old.diff(&now, Idiom::default())); + } + + #[test] + fn diff_replace_embedded() { + let old = Value::parse("{ test: { other: 'test' } }"); + let now = Value::parse("{ test: { other: false } }"); + let res = Value::parse("[{ op: 'replace', path: '/test/other', value: false }]"); + assert_eq!(res.to_operations().unwrap(), old.diff(&now, Idiom::default())); + } + + #[test] + fn diff_change_text() { + let old = Value::parse("{ test: { other: 'test' } }"); + let now = Value::parse("{ test: { other: 'text' } }"); + let res = Value::parse( + "[{ op: 'change', path: '/test/other', value: '@@ -1,4 +1,4 @@\n te\n-s\n+x\n t\n' }]", + ); + assert_eq!(res.to_operations().unwrap(), old.diff(&now, Idiom::default())); + } +} diff --git a/core/src/sql/v2/value/each.rs b/core/src/sql/v2/value/each.rs new file mode 100644 index 00000000..77f8d34f --- /dev/null +++ b/core/src/sql/v2/value/each.rs @@ -0,0 +1,142 @@ +use crate::sql::idiom::Idiom; +use crate::sql::part::Next; +use crate::sql::part::Part; +use crate::sql::value::Value; + +impl Value { + pub(crate) fn each(&self, path: &[Part]) -> Vec { + self._each(path, Idiom::default()) + } + fn _each(&self, path: &[Part], prev: Idiom) -> Vec { + match path.first() { + // Get the current path part + Some(p) => match self { + // Current path part is an object + Value::Object(v) => match p { + Part::Field(f) => match v.get(f as &str) { + Some(v) => v._each(path.next(), prev.push(p.clone())), + None => vec![], + }, + Part::All => self._each(path.next(), prev.push(p.clone())), + _ => vec![], + }, + // Current path part is an array + Value::Array(v) => match p { + Part::All => v + .iter() + .enumerate() + .flat_map(|(i, v)| v._each(path.next(), prev.clone().push(Part::from(i)))) + .collect::>(), + Part::First => match v.first() { + Some(v) => v._each(path.next(), prev.push(p.clone())), + None => vec![], + }, + Part::Last => match v.last() { + Some(v) => v._each(path.next(), prev.push(p.clone())), + None => vec![], + }, + Part::Index(i) => match v.get(i.to_usize()) { + Some(v) => v._each(path.next(), prev.push(p.clone())), + None => vec![], + }, + _ => v + .iter() + .enumerate() + .flat_map(|(i, v)| v._each(path.next(), prev.clone().push(Part::from(i)))) + .collect::>(), + }, + // Ignore everything else + _ => vec![], + }, + // No more parts so get the value + None => vec![prev], + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::sql::idiom::Idiom; + use crate::syn::Parse; + + #[test] + fn each_none() { + let idi = Idiom::default(); + let val = Value::parse("{ test: { other: null, something: 123 } }"); + let res = vec![Idiom::default()]; + assert_eq!(res, val.each(&idi)); + assert_eq!(val.pick(&res[0]), Value::parse("{ test: { other: null, something: 123 } }")); + } + + #[test] + fn each_basic() { + let idi = Idiom::parse("test.something"); + let val = Value::parse("{ test: { other: null, something: 123 } }"); + let res = vec![Idiom::parse("test.something")]; + assert_eq!(res, val.each(&idi)); + assert_eq!(val.pick(&res[0]), Value::from(123)); + } + + #[test] + fn each_array() { + let idi = Idiom::parse("test.something"); + let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }"); + let res = vec![Idiom::parse("test.something")]; + assert_eq!(res, val.each(&idi)); + assert_eq!(val.pick(&res[0]), Value::parse("[{ age: 34 }, { age: 36 }]")); + } + + #[test] + fn each_array_field() { + let idi = Idiom::parse("test.something[*].age"); + let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }"); + let res = + vec![Idiom::parse("test.something[0].age"), Idiom::parse("test.something[1].age")]; + assert_eq!(res, val.each(&idi)); + assert_eq!(val.pick(&res[0]), Value::from(34)); + assert_eq!(val.pick(&res[1]), Value::from(36)); + } + + #[test] + fn each_array_field_embedded() { + let idi = Idiom::parse("test.something[*].tags"); + let val = Value::parse("{ test: { something: [{ age: 34, tags: ['code', 'databases'] }, { age: 36, tags: ['design', 'operations'] }] } }"); + let res = + vec![Idiom::parse("test.something[0].tags"), Idiom::parse("test.something[1].tags")]; + assert_eq!(res, val.each(&idi)); + assert_eq!(val.pick(&res[0]), Value::parse("['code', 'databases']")); + assert_eq!(val.pick(&res[1]), Value::parse("['design', 'operations']")); + } + + #[test] + fn each_array_field_embedded_index() { + let idi = Idiom::parse("test.something[*].tags[1]"); + let val = Value::parse("{ test: { something: [{ age: 34, tags: ['code', 'databases'] }, { age: 36, tags: ['design', 'operations'] }] } }"); + let res = vec![ + Idiom::parse("test.something[0].tags[1]"), + Idiom::parse("test.something[1].tags[1]"), + ]; + assert_eq!(res, val.each(&idi)); + assert_eq!(val.pick(&res[0]), Value::from("databases")); + assert_eq!(val.pick(&res[1]), Value::from("operations")); + } + + #[test] + fn each_array_field_embedded_index_all() { + let idi = Idiom::parse("test.something[*].tags[*]"); + let val = Value::parse("{ test: { something: [{ age: 34, tags: ['code', 'databases'] }, { age: 36, tags: ['design', 'operations'] }] } }"); + let res = vec![ + Idiom::parse("test.something[0].tags[0]"), + Idiom::parse("test.something[0].tags[1]"), + Idiom::parse("test.something[1].tags[0]"), + Idiom::parse("test.something[1].tags[1]"), + ]; + assert_eq!(res, val.each(&idi)); + assert_eq!(val.pick(&res[0]), Value::from("code")); + assert_eq!(val.pick(&res[1]), Value::from("databases")); + assert_eq!(val.pick(&res[2]), Value::from("design")); + assert_eq!(val.pick(&res[3]), Value::from("operations")); + } +} diff --git a/core/src/sql/v2/value/every.rs b/core/src/sql/v2/value/every.rs new file mode 100644 index 00000000..b66c162b --- /dev/null +++ b/core/src/sql/v2/value/every.rs @@ -0,0 +1,149 @@ +use crate::sql::idiom::Idiom; +use crate::sql::part::Part; +use crate::sql::value::Value; + +impl Value { + pub(crate) fn every(&self, path: Option<&[Part]>, steps: bool, arrays: bool) -> Vec { + match path { + Some(path) => self.pick(path)._every(steps, arrays, Idiom::from(path)), + None => self._every(steps, arrays, Idiom::default()), + } + } + fn _every(&self, steps: bool, arrays: bool, mut prev: Idiom) -> Vec { + match self { + // Current path part is an object and is not empty + Value::Object(v) if !v.is_empty() => { + // Remove any trailing * path parts + prev.remove_trailing_all(); + // Check if we should log intermediary nodes + match steps { + // Let's log all intermediary nodes + true if !prev.is_empty() => Some(prev.clone()) + .into_iter() + .chain(v.iter().flat_map(|(k, v)| { + let p = Part::from(k.to_owned()); + v._every(steps, arrays, prev.clone().push(p)) + })) + .collect::>(), + // Let's not log intermediary nodes + _ => v + .iter() + .flat_map(|(k, v)| { + let p = Part::from(k.to_owned()); + v._every(steps, arrays, prev.clone().push(p)) + }) + .collect::>(), + } + } + // Current path part is an array and is not empty + Value::Array(v) if !v.is_empty() => { + // Remove any trailing * path parts + prev.remove_trailing_all(); + // Check if we should log individual array items + match arrays { + // Let's log all individual array items + true => std::iter::once(prev.clone()) + .chain(v.iter().enumerate().rev().flat_map(|(i, v)| { + let p = Part::from(i.to_owned()); + v._every(steps, arrays, prev.clone().push(p)) + })) + .collect::>(), + // Let's not log individual array items + false => vec![prev], + } + } + // Process everything else + _ => vec![prev], + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::sql::idiom::Idiom; + use crate::syn::Parse; + + #[test] + fn every_with_empty_objects_arrays() { + let val = Value::parse("{ test: {}, status: false, something: {age: 45}, tags: []}"); + let res = vec![ + Idiom::parse("something.age"), + Idiom::parse("status"), + Idiom::parse("tags"), + Idiom::parse("test"), + ]; + assert_eq!(res, val.every(None, false, false)); + } + + #[test] + fn every_without_array_indexes() { + let val = Value::parse("{ test: { something: [{ age: 34, tags: ['code', 'databases'] }, { age: 36, tags: ['design', 'operations'] }] } }"); + let res = vec![Idiom::parse("test.something")]; + assert_eq!(res, val.every(None, false, false)); + } + + #[test] + fn every_including_array_indexes() { + let val = Value::parse("{ test: { something: [{ age: 34, tags: ['code', 'databases'] }, { age: 36, tags: ['design', 'operations'] }] } }"); + let res = vec![ + Idiom::parse("test.something"), + Idiom::parse("test.something[1].age"), + Idiom::parse("test.something[1].tags"), + Idiom::parse("test.something[1].tags[1]"), + Idiom::parse("test.something[1].tags[0]"), + Idiom::parse("test.something[0].age"), + Idiom::parse("test.something[0].tags"), + Idiom::parse("test.something[0].tags[1]"), + Idiom::parse("test.something[0].tags[0]"), + ]; + assert_eq!(res, val.every(None, false, true)); + } + + #[test] + fn every_including_intermediary_nodes_without_array_indexes() { + let val = Value::parse("{ test: { something: [{ age: 34, tags: ['code', 'databases'] }, { age: 36, tags: ['design', 'operations'] }] } }"); + let res = vec![Idiom::parse("test"), Idiom::parse("test.something")]; + assert_eq!(res, val.every(None, true, false)); + } + + #[test] + fn every_including_intermediary_nodes_including_array_indexes() { + let val = Value::parse("{ test: { something: [{ age: 34, tags: ['code', 'databases'] }, { age: 36, tags: ['design', 'operations'] }] } }"); + let res = vec![ + Idiom::parse("test"), + Idiom::parse("test.something"), + Idiom::parse("test.something[1]"), + Idiom::parse("test.something[1].age"), + Idiom::parse("test.something[1].tags"), + Idiom::parse("test.something[1].tags[1]"), + Idiom::parse("test.something[1].tags[0]"), + Idiom::parse("test.something[0]"), + Idiom::parse("test.something[0].age"), + Idiom::parse("test.something[0].tags"), + Idiom::parse("test.something[0].tags[1]"), + Idiom::parse("test.something[0].tags[0]"), + ]; + assert_eq!(res, val.every(None, true, true)); + } + + #[test] + fn every_including_intermediary_nodes_including_array_indexes_ending_all() { + let val = Value::parse("{ test: { something: [{ age: 34, tags: ['code', 'databases'] }, { age: 36, tags: ['design', 'operations'] }] } }"); + let res = vec![ + Idiom::parse("test.something"), + Idiom::parse("test.something[1]"), + Idiom::parse("test.something[1].age"), + Idiom::parse("test.something[1].tags"), + Idiom::parse("test.something[1].tags[1]"), + Idiom::parse("test.something[1].tags[0]"), + Idiom::parse("test.something[0]"), + Idiom::parse("test.something[0].age"), + Idiom::parse("test.something[0].tags"), + Idiom::parse("test.something[0].tags[1]"), + Idiom::parse("test.something[0].tags[0]"), + ]; + assert_eq!(res, val.every(Some(&Idiom::parse("test.something.*")), true, true)); + } +} diff --git a/core/src/sql/v2/value/extend.rs b/core/src/sql/v2/value/extend.rs new file mode 100644 index 00000000..8e602723 --- /dev/null +++ b/core/src/sql/v2/value/extend.rs @@ -0,0 +1,58 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::err::Error; +use crate::sql::array::Uniq; +use crate::sql::part::Part; +use crate::sql::value::Value; + +impl Value { + pub(crate) async fn extend( + &mut self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + path: &[Part], + val: Value, + ) -> Result<(), Error> { + match self.get(ctx, opt, txn, None, path).await? { + Value::Array(v) => match val { + Value::Array(x) => self.set(ctx, opt, txn, path, Value::from((v + x).uniq())).await, + x => self.set(ctx, opt, txn, path, Value::from((v + x).uniq())).await, + }, + Value::None => match val { + Value::Array(x) => self.set(ctx, opt, txn, path, Value::from(x)).await, + x => self.set(ctx, opt, txn, path, Value::from(vec![x])).await, + }, + _ => Ok(()), + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::dbs::test::mock; + use crate::sql::idiom::Idiom; + use crate::syn::Parse; + + #[tokio::test] + async fn extend_array_value() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test"); + let mut val = Value::parse("{ test: [100, 200, 300] }"); + let res = Value::parse("{ test: [100, 200, 300] }"); + val.extend(&ctx, &opt, &txn, &idi, Value::from(200)).await.unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn extend_array_array() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test"); + let mut val = Value::parse("{ test: [100, 200, 300] }"); + let res = Value::parse("{ test: [100, 200, 300, 400, 500] }"); + val.extend(&ctx, &opt, &txn, &idi, Value::parse("[100, 300, 400, 500]")).await.unwrap(); + assert_eq!(res, val); + } +} diff --git a/core/src/sql/v2/value/fetch.rs b/core/src/sql/v2/value/fetch.rs new file mode 100644 index 00000000..3466bdb3 --- /dev/null +++ b/core/src/sql/v2/value/fetch.rs @@ -0,0 +1,148 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::err::Error; +use crate::sql::edges::Edges; +use crate::sql::field::{Field, Fields}; +use crate::sql::part::Next; +use crate::sql::part::Part; +use crate::sql::statements::select::SelectStatement; +use crate::sql::value::{Value, Values}; +use async_recursion::async_recursion; +use futures::future::try_join_all; + +impl Value { + #[cfg_attr(not(target_arch = "wasm32"), async_recursion)] + #[cfg_attr(target_arch = "wasm32", async_recursion(?Send))] + pub(crate) async fn fetch( + &mut self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + path: &[Part], + ) -> Result<(), Error> { + match path.first() { + // Get the current path part + Some(p) => match self { + // Current path part is an object + Value::Object(v) => match p { + Part::Graph(_) => match v.rid() { + Some(v) => Value::Thing(v).fetch(ctx, opt, txn, path.next()).await, + None => Ok(()), + }, + Part::Field(f) => match v.get_mut(f as &str) { + Some(v) => v.fetch(ctx, opt, txn, path.next()).await, + None => Ok(()), + }, + Part::Index(i) => match v.get_mut(&i.to_string()) { + Some(v) => v.fetch(ctx, opt, txn, path.next()).await, + None => Ok(()), + }, + Part::All => self.fetch(ctx, opt, txn, path.next()).await, + _ => Ok(()), + }, + // Current path part is an array + Value::Array(v) => match p { + Part::All => { + let path = path.next(); + let futs = v.iter_mut().map(|v| v.fetch(ctx, opt, txn, path)); + try_join_all(futs).await?; + Ok(()) + } + Part::First => match v.first_mut() { + Some(v) => v.fetch(ctx, opt, txn, path.next()).await, + None => Ok(()), + }, + Part::Last => match v.last_mut() { + Some(v) => v.fetch(ctx, opt, txn, path.next()).await, + None => Ok(()), + }, + Part::Index(i) => match v.get_mut(i.to_usize()) { + Some(v) => v.fetch(ctx, opt, txn, path.next()).await, + None => Ok(()), + }, + Part::Where(w) => { + let path = path.next(); + for v in v.iter_mut() { + let cur = v.into(); + if w.compute(ctx, opt, txn, Some(&cur)).await?.is_truthy() { + v.fetch(ctx, opt, txn, path).await?; + } + } + Ok(()) + } + _ => { + let futs = v.iter_mut().map(|v| v.fetch(ctx, opt, txn, path)); + try_join_all(futs).await?; + Ok(()) + } + }, + // Current path part is a thing + Value::Thing(v) => { + // Clone the thing + let val = v.clone(); + // Fetch the remote embedded record + match p { + // This is a graph traversal expression + Part::Graph(g) => { + let stm = SelectStatement { + expr: Fields(vec![Field::All], false), + what: Values(vec![Value::from(Edges { + from: val, + dir: g.dir.clone(), + what: g.what.clone(), + })]), + cond: g.cond.clone(), + ..SelectStatement::default() + }; + *self = stm + .compute(ctx, opt, txn, None) + .await? + .all() + .get(ctx, opt, txn, None, path.next()) + .await? + .flatten() + .ok()?; + Ok(()) + } + // This is a remote field expression + _ => { + let stm = SelectStatement { + expr: Fields(vec![Field::All], false), + what: Values(vec![Value::from(val)]), + ..SelectStatement::default() + }; + *self = stm.compute(ctx, opt, txn, None).await?.first(); + Ok(()) + } + } + } + // Ignore everything else + _ => Ok(()), + }, + // No more parts so get the value + None => match self { + // Current path part is an array + Value::Array(v) => { + let futs = v.iter_mut().map(|v| v.fetch(ctx, opt, txn, path)); + try_join_all(futs).await?; + Ok(()) + } + // Current path part is a thing + Value::Thing(v) => { + // Clone the thing + let val = v.clone(); + // Fetch the remote embedded record + let stm = SelectStatement { + expr: Fields(vec![Field::All], false), + what: Values(vec![Value::from(val)]), + ..SelectStatement::default() + }; + *self = stm.compute(ctx, opt, txn, None).await?.first(); + Ok(()) + } + // Ignore everything else + _ => Ok(()), + }, + } + } +} diff --git a/core/src/sql/v2/value/first.rs b/core/src/sql/v2/value/first.rs new file mode 100644 index 00000000..15b4dcbf --- /dev/null +++ b/core/src/sql/v2/value/first.rs @@ -0,0 +1,8 @@ +use crate::sql::part::Part; +use crate::sql::value::Value; + +impl Value { + pub fn first(&self) -> Self { + self.pick(&[Part::First]) + } +} diff --git a/core/src/sql/v2/value/flatten.rs b/core/src/sql/v2/value/flatten.rs new file mode 100644 index 00000000..7f41357f --- /dev/null +++ b/core/src/sql/v2/value/flatten.rs @@ -0,0 +1,19 @@ +use crate::sql::array::Array; +use crate::sql::value::Value; + +impl Value { + pub fn flatten(self) -> Self { + match self { + Value::Array(v) => { + v.0.into_iter() + .flat_map(|v| match v { + Value::Array(v) => v, + _ => Array::from(v), + }) + .collect::>() + .into() + } + v => v, + } + } +} diff --git a/core/src/sql/v2/value/generate.rs b/core/src/sql/v2/value/generate.rs new file mode 100644 index 00000000..e4db2dec --- /dev/null +++ b/core/src/sql/v2/value/generate.rs @@ -0,0 +1,69 @@ +use crate::err::Error; +use crate::sql::id::Id; +use crate::sql::table::Table; +use crate::sql::thing::Thing; +use crate::sql::value::Value; + +impl Value { + pub(crate) fn generate(self, tb: &Table, retable: bool) -> Result { + match self { + // There is a floating point number for the id field + Value::Number(id) if id.is_float() => Ok(Thing { + tb: tb.to_string(), + id: id.as_int().into(), + }), + // There is an integer number for the id field + Value::Number(id) if id.is_int() => Ok(Thing { + tb: tb.to_string(), + id: id.as_int().into(), + }), + // There is a string for the id field + Value::Strand(id) if !id.is_empty() => Ok(Thing { + tb: tb.to_string(), + id: id.into(), + }), + // There is an object for the id field + Value::Object(id) => Ok(Thing { + tb: tb.to_string(), + id: id.into(), + }), + // There is an array for the id field + Value::Array(id) => Ok(Thing { + tb: tb.to_string(), + id: id.into(), + }), + // There is a UUID for the id field + Value::Uuid(id) => Ok(Thing { + tb: tb.to_string(), + id: id.into(), + }), + // There is no record id field + Value::None => Ok(Thing { + tb: tb.to_string(), + id: Id::rand(), + }), + // There is a record id defined + Value::Thing(id) => match retable { + // Let's re-table this record id + true => Ok(Thing { + tb: tb.to_string(), + id: id.id, + }), + // Let's use the specified record id + false => match tb.0 == id.tb { + // The record is from the same table + true => Ok(id), + // The record id is from another table + false => Ok(Thing { + tb: tb.to_string(), + id: id.id, + }), + }, + }, + // Any other value is wrong + id => Err(Error::IdInvalid { + value: id.to_string(), + }), + } + } +} diff --git a/core/src/sql/v2/value/get.rs b/core/src/sql/v2/value/get.rs new file mode 100644 index 00000000..d7e5067b --- /dev/null +++ b/core/src/sql/v2/value/get.rs @@ -0,0 +1,433 @@ +use crate::cnf::MAX_COMPUTATION_DEPTH; +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::doc::CursorDoc; +use crate::err::Error; +use crate::exe::try_join_all_buffered; +use crate::sql::edges::Edges; +use crate::sql::field::{Field, Fields}; +use crate::sql::id::Id; +use crate::sql::part::Next; +use crate::sql::part::Part; +use crate::sql::paths::ID; +use crate::sql::statements::select::SelectStatement; +use crate::sql::thing::Thing; +use crate::sql::value::{Value, Values}; +use async_recursion::async_recursion; + +impl Value { + /// Asynchronous method for getting a local or remote field from a `Value` + #[cfg_attr(not(target_arch = "wasm32"), async_recursion)] + #[cfg_attr(target_arch = "wasm32", async_recursion(?Send))] + pub(crate) async fn get( + &self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + doc: Option<&'async_recursion CursorDoc<'_>>, + path: &[Part], + ) -> Result { + // Limit recursion depth. + if path.len() > (*MAX_COMPUTATION_DEPTH).into() { + return Err(Error::ComputationDepthExceeded); + } + match path.first() { + // Get the current value at the path + Some(p) => match self { + // Current value at path is a geometry + Value::Geometry(v) => match p { + // If this is the 'type' field then continue + Part::Field(f) if f.is_type() => { + Value::from(v.as_type()).get(ctx, opt, txn, doc, path.next()).await + } + // If this is the 'coordinates' field then continue + Part::Field(f) if f.is_coordinates() && v.is_geometry() => { + v.as_coordinates().get(ctx, opt, txn, doc, path.next()).await + } + // If this is the 'geometries' field then continue + Part::Field(f) if f.is_geometries() && v.is_collection() => { + v.as_coordinates().get(ctx, opt, txn, doc, path.next()).await + } + // Otherwise return none + _ => Ok(Value::None), + }, + // Current value at path is a future + Value::Future(v) => { + // Check how many path parts are remaining + match path.len() { + // No further embedded fields, so just return this + 0 => Ok(Value::Future(v.clone())), + // Process the future and fetch the embedded field + _ => { + // Ensure the future is processed + let fut = &opt.new_with_futures(true); + // Get the future return value + let val = v.compute(ctx, fut, txn, doc).await?; + // Fetch the embedded field + val.get(ctx, opt, txn, doc, path).await + } + } + } + // Current value at path is an object + Value::Object(v) => match p { + // If requesting an `id` field, check if it is a complex Record ID + Part::Field(f) if f.is_id() && path.len() > 1 => match v.get(f.as_str()) { + Some(Value::Thing(Thing { + id: Id::Object(v), + .. + })) => Value::Object(v.clone()).get(ctx, opt, txn, doc, path.next()).await, + Some(Value::Thing(Thing { + id: Id::Array(v), + .. + })) => Value::Array(v.clone()).get(ctx, opt, txn, doc, path.next()).await, + Some(v) => v.get(ctx, opt, txn, doc, path.next()).await, + None => Ok(Value::None), + }, + Part::Graph(_) => match v.rid() { + Some(v) => Value::Thing(v).get(ctx, opt, txn, doc, path).await, + None => Ok(Value::None), + }, + Part::Field(f) => match v.get(f.as_str()) { + Some(v) => v.get(ctx, opt, txn, doc, path.next()).await, + None => Ok(Value::None), + }, + Part::Index(i) => match v.get(&i.to_string()) { + Some(v) => v.get(ctx, opt, txn, doc, path.next()).await, + None => Ok(Value::None), + }, + Part::Value(x) => match x.compute(ctx, opt, txn, doc).await? { + Value::Strand(f) => match v.get(f.as_str()) { + Some(v) => v.get(ctx, opt, txn, doc, path.next()).await, + None => Ok(Value::None), + }, + _ => Ok(Value::None), + }, + Part::All => self.get(ctx, opt, txn, doc, path.next()).await, + _ => Ok(Value::None), + }, + // Current value at path is an array + Value::Array(v) => match p { + // Current path is an `*` part + Part::All | Part::Flatten => { + let path = path.next(); + let futs = v.iter().map(|v| v.get(ctx, opt, txn, doc, path)); + try_join_all_buffered(futs).await.map(Into::into) + } + Part::First => match v.first() { + Some(v) => v.get(ctx, opt, txn, doc, path.next()).await, + None => Ok(Value::None), + }, + Part::Last => match v.last() { + Some(v) => v.get(ctx, opt, txn, doc, path.next()).await, + None => Ok(Value::None), + }, + Part::Index(i) => match v.get(i.to_usize()) { + Some(v) => v.get(ctx, opt, txn, doc, path.next()).await, + None => Ok(Value::None), + }, + Part::Where(w) => { + let mut a = Vec::new(); + for v in v.iter() { + let cur = v.into(); + if w.compute(ctx, opt, txn, Some(&cur)).await?.is_truthy() { + a.push(v.clone()); + } + } + Value::from(a).get(ctx, opt, txn, doc, path.next()).await + } + Part::Value(x) => match x.compute(ctx, opt, txn, doc).await? { + Value::Number(i) => match v.get(i.to_usize()) { + Some(v) => v.get(ctx, opt, txn, doc, path.next()).await, + None => Ok(Value::None), + }, + _ => Ok(Value::None), + }, + _ => { + let futs = v.iter().map(|v| v.get(ctx, opt, txn, doc, path)); + try_join_all_buffered(futs).await.map(Into::into) + } + }, + // Current value at path is an edges + Value::Edges(v) => { + // Clone the thing + let val = v.clone(); + // Check how many path parts are remaining + match path.len() { + // No remote embedded fields, so just return this + 0 => Ok(Value::Edges(val)), + // Remote embedded field, so fetch the thing + _ => { + let stm = SelectStatement { + expr: Fields(vec![Field::All], false), + what: Values(vec![Value::from(val)]), + ..SelectStatement::default() + }; + stm.compute(ctx, opt, txn, None) + .await? + .first() + .get(ctx, opt, txn, None, path) + .await + } + } + } + // Current value at path is a thing + Value::Thing(v) => { + // Clone the thing + let val = v.clone(); + // Check how many path parts are remaining + match path.len() { + // No remote embedded fields, so just return this + 0 => Ok(Value::Thing(val)), + // Remote embedded field, so fetch the thing + _ => match p { + // This is a graph traversal expression + Part::Graph(g) => { + let stm = SelectStatement { + expr: Fields(vec![Field::All], false), + what: Values(vec![Value::from(Edges { + from: val, + dir: g.dir.clone(), + what: g.what.clone(), + })]), + cond: g.cond.clone(), + ..SelectStatement::default() + }; + match path.len() { + 1 => stm + .compute(ctx, opt, txn, None) + .await? + .all() + .get(ctx, opt, txn, None, ID.as_ref()) + .await? + .flatten() + .ok(), + _ => stm + .compute(ctx, opt, txn, None) + .await? + .all() + .get(ctx, opt, txn, None, path.next()) + .await? + .flatten() + .ok(), + } + } + // This is a remote field expression + _ => { + let stm = SelectStatement { + expr: Fields(vec![Field::All], false), + what: Values(vec![Value::from(val)]), + ..SelectStatement::default() + }; + stm.compute(ctx, opt, txn, None) + .await? + .first() + .get(ctx, opt, txn, None, path) + .await + } + }, + } + } + v => { + if matches!(p, Part::Flatten) { + v.get(ctx, opt, txn, None, path.next()).await + } else { + // Ignore everything else + Ok(Value::None) + } + } + }, + // No more parts so get the value + None => Ok(self.clone()), + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::dbs::test::mock; + use crate::sql::id::Id; + use crate::sql::idiom::Idiom; + use crate::sql::thing::Thing; + use crate::syn::Parse; + + #[tokio::test] + async fn get_none() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::default(); + let val = Value::parse("{ test: { other: null, something: 123 } }"); + let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn get_basic() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test.something"); + let val = Value::parse("{ test: { other: null, something: 123 } }"); + let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap(); + assert_eq!(res, Value::from(123)); + } + + #[tokio::test] + async fn get_basic_deep_ok() { + let (ctx, opt, txn) = mock().await; + let depth = 20; + let idi = Idiom::parse(&format!("{}something", "test.".repeat(depth))); + let val = Value::parse(&format!( + "{} {{ other: null, something: 123 {} }}", + "{ test: ".repeat(depth), + "}".repeat(depth) + )); + let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap(); + assert_eq!(res, Value::from(123)); + } + + #[tokio::test] + async fn get_basic_deep_ko() { + let (ctx, opt, txn) = mock().await; + let depth = 2000; + let idi = Idiom::parse(&format!("{}something", "test.".repeat(depth))); + let val = Value::parse("{}"); // A deep enough object cannot be parsed. + let err = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap_err(); + assert!( + matches!(err, Error::ComputationDepthExceeded), + "expected computation depth exceeded, got {:?}", + err + ); + } + + #[tokio::test] + async fn get_thing() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test.other"); + let val = Value::parse("{ test: { other: test:tobie, something: 123 } }"); + let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap(); + assert_eq!( + res, + Value::from(Thing { + tb: String::from("test"), + id: Id::from("tobie") + }) + ); + } + + #[tokio::test] + async fn get_array() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test.something[1]"); + let val = Value::parse("{ test: { something: [123, 456, 789] } }"); + let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap(); + assert_eq!(res, Value::from(456)); + } + + #[tokio::test] + async fn get_array_thing() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test.something[1]"); + let val = Value::parse("{ test: { something: [test:tobie, test:jaime] } }"); + let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap(); + assert_eq!( + res, + Value::from(Thing { + tb: String::from("test"), + id: Id::from("jaime") + }) + ); + } + + #[tokio::test] + async fn get_array_field() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test.something[1].age"); + let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }"); + let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap(); + assert_eq!(res, Value::from(36)); + } + + #[tokio::test] + async fn get_array_fields() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test.something[*].age"); + let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }"); + let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap(); + assert_eq!(res, Value::from(vec![34, 36])); + } + + #[tokio::test] + async fn get_array_fields_flat() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test.something.age"); + let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }"); + let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap(); + assert_eq!(res, Value::from(vec![34, 36])); + } + + #[tokio::test] + async fn get_array_where_field() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test.something[WHERE age > 35].age"); + let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }"); + let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap(); + assert_eq!(res, Value::from(vec![36])); + } + + #[tokio::test] + async fn get_array_where_fields() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test.something[WHERE age > 35]"); + let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }"); + let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap(); + assert_eq!( + res, + Value::from(vec![Value::from(map! { + "age".to_string() => Value::from(36), + })]) + ); + } + + #[tokio::test] + async fn get_array_where_fields_array_index() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test.something[WHERE age > 30][0]"); + let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }"); + let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap(); + assert_eq!( + res, + Value::from(map! { + "age".to_string() => Value::from(34), + }) + ); + } + + #[tokio::test] + async fn get_future_embedded_field() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test.something[WHERE age > 35]"); + let val = Value::parse("{ test: { { something: [{ age: 34 }, { age: 36 }] } } }"); + let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap(); + assert_eq!( + res, + Value::from(vec![Value::from(map! { + "age".to_string() => Value::from(36), + })]) + ); + } + + #[tokio::test] + async fn get_future_embedded_field_with_reference() { + let (ctx, opt, txn) = mock().await; + let doc = Value::parse("{ name: 'Tobie', something: [{ age: 34 }, { age: 36 }] }"); + let idi = Idiom::parse("test.something[WHERE age > 35]"); + let val = Value::parse("{ test: { { something: something } } }"); + let cur = (&doc).into(); + let res = val.get(&ctx, &opt, &txn, Some(&cur), &idi).await.unwrap(); + assert_eq!( + res, + Value::from(vec![Value::from(map! { + "age".to_string() => Value::from(36), + })]) + ); + } +} diff --git a/core/src/sql/v2/value/inc.rs b/core/src/sql/v2/value/inc.rs new file mode 100644 index 00000000..cd690879 --- /dev/null +++ b/core/src/sql/v2/value/inc.rs @@ -0,0 +1,79 @@ +use crate::sql::number::Number; +use crate::sql::part::Part; +use crate::sql::value::Value; + +impl Value { + /// Synchronous method for incrementing a field in a `Value` + pub(crate) fn inc(&mut self, path: &[Part], val: Value) { + match self.pick(path) { + Value::Number(v) => { + if let Value::Number(x) = val { + self.put(path, Value::from(v + x)) + } + } + Value::Array(v) => match val { + Value::Array(x) => self.put(path, Value::from(v + x)), + x => self.put(path, Value::from(v + x)), + }, + Value::None => match val { + Value::Number(x) => self.put(path, Value::from(Number::from(0) + x)), + Value::Array(x) => self.put(path, Value::from(x)), + x => self.put(path, Value::from(vec![x])), + }, + _ => (), + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::sql::idiom::Idiom; + use crate::syn::Parse; + + #[tokio::test] + async fn increment_none() { + let idi = Idiom::parse("other"); + let mut val = Value::parse("{ test: 100 }"); + let res = Value::parse("{ test: 100, other: +10 }"); + val.inc(&idi, Value::from(10)); + assert_eq!(res, val); + } + + #[tokio::test] + async fn increment_number() { + let idi = Idiom::parse("test"); + let mut val = Value::parse("{ test: 100 }"); + let res = Value::parse("{ test: 110 }"); + val.inc(&idi, Value::from(10)); + assert_eq!(res, val); + } + + #[tokio::test] + async fn increment_array_number() { + let idi = Idiom::parse("test[1]"); + let mut val = Value::parse("{ test: [100, 200, 300] }"); + let res = Value::parse("{ test: [100, 210, 300] }"); + val.inc(&idi, Value::from(10)); + assert_eq!(res, val); + } + + #[tokio::test] + async fn increment_array_value() { + let idi = Idiom::parse("test"); + let mut val = Value::parse("{ test: [100, 200, 300] }"); + let res = Value::parse("{ test: [100, 200, 300, 200] }"); + val.inc(&idi, Value::from(200)); + assert_eq!(res, val); + } + + #[tokio::test] + async fn increment_array_array() { + let idi = Idiom::parse("test"); + let mut val = Value::parse("{ test: [100, 200, 300] }"); + let res = Value::parse("{ test: [100, 200, 300, 100, 300, 400, 500] }"); + val.inc(&idi, Value::parse("[100, 300, 400, 500]")); + assert_eq!(res, val); + } +} diff --git a/core/src/sql/v2/value/increment.rs b/core/src/sql/v2/value/increment.rs new file mode 100644 index 00000000..83634fe7 --- /dev/null +++ b/core/src/sql/v2/value/increment.rs @@ -0,0 +1,96 @@ +use crate::ctx::Context; +use crate::dbs::{Options, Transaction}; +use crate::err::Error; +use crate::sql::number::Number; +use crate::sql::part::Part; +use crate::sql::value::Value; + +impl Value { + /// Asynchronous method for incrementing a field in a `Value` + pub(crate) async fn increment( + &mut self, + ctx: &Context<'_>, + opt: &Options, + txn: &Transaction, + path: &[Part], + val: Value, + ) -> Result<(), Error> { + match self.get(ctx, opt, txn, None, path).await? { + Value::Number(v) => match val { + Value::Number(x) => self.set(ctx, opt, txn, path, Value::from(v + x)).await, + _ => Ok(()), + }, + Value::Array(v) => match val { + Value::Array(x) => self.set(ctx, opt, txn, path, Value::from(v + x)).await, + x => self.set(ctx, opt, txn, path, Value::from(v + x)).await, + }, + Value::None => match val { + Value::Number(x) => { + self.set(ctx, opt, txn, path, Value::from(Number::from(0) + x)).await + } + Value::Array(x) => self.set(ctx, opt, txn, path, Value::from(x)).await, + x => self.set(ctx, opt, txn, path, Value::from(vec![x])).await, + }, + _ => Ok(()), + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::dbs::test::mock; + use crate::sql::idiom::Idiom; + use crate::syn::Parse; + + #[tokio::test] + async fn increment_none() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("other"); + let mut val = Value::parse("{ test: 100 }"); + let res = Value::parse("{ test: 100, other: +10 }"); + val.increment(&ctx, &opt, &txn, &idi, Value::from(10)).await.unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn increment_number() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test"); + let mut val = Value::parse("{ test: 100 }"); + let res = Value::parse("{ test: 110 }"); + val.increment(&ctx, &opt, &txn, &idi, Value::from(10)).await.unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn increment_array_number() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test[1]"); + let mut val = Value::parse("{ test: [100, 200, 300] }"); + let res = Value::parse("{ test: [100, 210, 300] }"); + val.increment(&ctx, &opt, &txn, &idi, Value::from(10)).await.unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn increment_array_value() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test"); + let mut val = Value::parse("{ test: [100, 200, 300] }"); + let res = Value::parse("{ test: [100, 200, 300, 200] }"); + val.increment(&ctx, &opt, &txn, &idi, Value::from(200)).await.unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn increment_array_array() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test"); + let mut val = Value::parse("{ test: [100, 200, 300] }"); + let res = Value::parse("{ test: [100, 200, 300, 100, 300, 400, 500] }"); + val.increment(&ctx, &opt, &txn, &idi, Value::parse("[100, 300, 400, 500]")).await.unwrap(); + assert_eq!(res, val); + } +} diff --git a/core/src/sql/v2/value/last.rs b/core/src/sql/v2/value/last.rs new file mode 100644 index 00000000..e4f02ef2 --- /dev/null +++ b/core/src/sql/v2/value/last.rs @@ -0,0 +1,8 @@ +use crate::sql::part::Part; +use crate::sql::value::Value; + +impl Value { + pub fn last(&self) -> Self { + self.pick(&[Part::Last]) + } +} diff --git a/core/src/sql/v2/value/merge.rs b/core/src/sql/v2/value/merge.rs new file mode 100644 index 00000000..c42c1774 --- /dev/null +++ b/core/src/sql/v2/value/merge.rs @@ -0,0 +1,82 @@ +use crate::err::Error; +use crate::sql::value::Value; + +impl Value { + pub(crate) fn merge(&mut self, val: Value) -> Result<(), Error> { + // If this value is not an object, then error + if !val.is_object() { + return Err(Error::InvalidMerge { + value: val, + }); + } + // Otherwise loop through every object field + for k in val.every(None, false, false).iter() { + match val.pick(k) { + Value::None => self.cut(k), + v => self.put(k, v), + } + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::syn::Parse; + + #[tokio::test] + async fn merge_none() { + let mut res = Value::parse( + "{ + name: { + first: 'Tobie', + last: 'Morgan Hitchcock', + initials: 'TMH', + }, + }", + ); + let none = Value::None; + match res.merge(none.clone()).unwrap_err() { + Error::InvalidMerge { + value, + } => assert_eq!(value, none), + error => panic!("unexpected error: {error:?}"), + } + } + + #[tokio::test] + async fn merge_basic() { + let mut res = Value::parse( + "{ + name: { + first: 'Tobie', + last: 'Morgan Hitchcock', + initials: 'TMH', + }, + }", + ); + let mrg = Value::parse( + "{ + name: { + title: 'Mr', + initials: NONE, + }, + tags: ['Rust', 'Golang', 'JavaScript'], + }", + ); + let val = Value::parse( + "{ + name: { + title: 'Mr', + first: 'Tobie', + last: 'Morgan Hitchcock', + }, + tags: ['Rust', 'Golang', 'JavaScript'], + }", + ); + res.merge(mrg).unwrap(); + assert_eq!(res, val); + } +} diff --git a/core/src/sql/v2/value/mod.rs b/core/src/sql/v2/value/mod.rs new file mode 100644 index 00000000..e3c31a67 --- /dev/null +++ b/core/src/sql/v2/value/mod.rs @@ -0,0 +1,35 @@ +pub use self::value::*; + +pub(super) mod serde; + +#[allow(clippy::module_inception)] +mod value; + +mod all; +mod changed; +mod clear; +mod compare; +mod cut; +mod decrement; +mod def; +mod del; +mod diff; +mod each; +mod every; +mod extend; +mod fetch; +mod first; +mod flatten; +mod generate; +mod get; +mod inc; +mod increment; +mod last; +mod merge; +mod patch; +mod pick; +mod put; +mod replace; +mod rid; +mod set; +mod walk; diff --git a/core/src/sql/v2/value/patch.rs b/core/src/sql/v2/value/patch.rs new file mode 100644 index 00000000..507736e4 --- /dev/null +++ b/core/src/sql/v2/value/patch.rs @@ -0,0 +1,238 @@ +use crate::err::Error; +use crate::sql::operation::Operation; +use crate::sql::value::Value; + +impl Value { + pub(crate) fn patch(&mut self, ops: Value) -> Result<(), Error> { + // This value is for test operation, value itself shouldn't change until all operations done. + // If test operations fails, nothing in value will be changed. + let mut tmp_val = self.clone(); + + for operation in ops.to_operations()?.into_iter() { + match operation { + Operation::Add { + path, + value, + } => match tmp_val.pick(&path) { + Value::Array(_) => tmp_val.inc(&path, value), + _ => tmp_val.put(&path, value), + }, + Operation::Remove { + path, + } => tmp_val.cut(&path), + Operation::Replace { + path, + value, + } => tmp_val.put(&path, value), + Operation::Change { + path, + value, + } => { + if let Value::Strand(p) = value { + if let Value::Strand(v) = tmp_val.pick(&path) { + let dmp = dmp::new(); + let pch = dmp.patch_from_text(p.as_string()).map_err(|e| { + Error::InvalidPatch { + message: format!("{e:?}"), + } + })?; + let (txt, _) = dmp.patch_apply(&pch, v.as_str()).map_err(|e| { + Error::InvalidPatch { + message: format!("{e:?}"), + } + })?; + let txt = txt.into_iter().collect::(); + tmp_val.put(&path, Value::from(txt)); + } + } + } + Operation::Copy { + path, + from, + } => { + let found_val = tmp_val.pick(&from); + tmp_val.put(&path, found_val); + } + Operation::Move { + path, + from, + } => { + let found_val = tmp_val.pick(&from); + tmp_val.put(&path, found_val); + tmp_val.cut(&from); + } + Operation::Test { + path, + value, + } => { + let found_val = tmp_val.pick(&path); + + if value != found_val { + return Err(Error::PatchTest { + expected: value.to_string(), + got: found_val.to_string(), + }); + } + } + } + } + + *self = tmp_val; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::syn::Parse; + + #[tokio::test] + async fn patch_add_simple() { + let mut val = Value::parse("{ test: { other: null, something: 123 } }"); + let ops = Value::parse("[{ op: 'add', path: '/temp', value: true }]"); + let res = Value::parse("{ test: { other: null, something: 123 }, temp: true }"); + val.patch(ops).unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn patch_remove_simple() { + let mut val = Value::parse("{ test: { other: null, something: 123 }, temp: true }"); + let ops = Value::parse("[{ op: 'remove', path: '/temp' }]"); + let res = Value::parse("{ test: { other: null, something: 123 } }"); + val.patch(ops).unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn patch_replace_simple() { + let mut val = Value::parse("{ test: { other: null, something: 123 }, temp: true }"); + let ops = Value::parse("[{ op: 'replace', path: '/temp', value: 'text' }]"); + let res = Value::parse("{ test: { other: null, something: 123 }, temp: 'text' }"); + val.patch(ops).unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn patch_change_simple() { + let mut val = Value::parse("{ test: { other: null, something: 123 }, temp: 'test' }"); + let ops = Value::parse( + "[{ op: 'change', path: '/temp', value: '@@ -1,4 +1,4 @@\n te\n-s\n+x\n t\n' }]", + ); + let res = Value::parse("{ test: { other: null, something: 123 }, temp: 'text' }"); + val.patch(ops).unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn patch_copy_simple() { + let mut val = Value::parse("{ test: 123, temp: true }"); + let ops = Value::parse("[{ op: 'copy', path: '/temp', from: '/test' }]"); + let res = Value::parse("{ test: 123, temp: 123 }"); + val.patch(ops).unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn patch_move_simple() { + let mut val = Value::parse("{ temp: true, some: 123 }"); + let ops = Value::parse("[{ op: 'move', path: '/other', from: '/temp' }]"); + let res = Value::parse("{ other: true, some: 123 }"); + val.patch(ops).unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn patch_test_simple() { + let mut val = Value::parse("{ test: { other: 'test', something: 123 }, temp: true }"); + let ops = Value::parse("[{ op: 'remove', path: '/test/something' }, { op: 'test', path: '/temp', value: true }]"); + let res = Value::parse("{ test: { other: 'test' }, temp: true }"); + val.patch(ops).unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn patch_add_embedded() { + let mut val = Value::parse("{ test: { other: null, something: 123 } }"); + let ops = Value::parse("[{ op: 'add', path: '/temp/test', value: true }]"); + let res = Value::parse("{ test: { other: null, something: 123 }, temp: { test: true } }"); + val.patch(ops).unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn patch_remove_embedded() { + let mut val = Value::parse("{ test: { other: null, something: 123 }, temp: true }"); + let ops = Value::parse("[{ op: 'remove', path: '/test/other' }]"); + let res = Value::parse("{ test: { something: 123 }, temp: true }"); + val.patch(ops).unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn patch_replace_embedded() { + let mut val = Value::parse("{ test: { other: null, something: 123 }, temp: true }"); + let ops = Value::parse("[{ op: 'replace', path: '/test/other', value: 'text' }]"); + let res = Value::parse("{ test: { other: 'text', something: 123 }, temp: true }"); + val.patch(ops).unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn patch_change_embedded() { + let mut val = Value::parse("{ test: { other: 'test', something: 123 }, temp: true }"); + let ops = Value::parse( + "[{ op: 'change', path: '/test/other', value: '@@ -1,4 +1,4 @@\n te\n-s\n+x\n t\n' }]", + ); + let res = Value::parse("{ test: { other: 'text', something: 123 }, temp: true }"); + val.patch(ops).unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn patch_copy_embedded() { + let mut val = Value::parse("{ test: { other: null }, temp: 123 }"); + let ops = Value::parse("[{ op: 'copy', path: '/test/other', from: '/temp' }]"); + let res = Value::parse("{ test: { other: 123 }, temp: 123 }"); + val.patch(ops).unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn patch_move_embedded() { + let mut val = Value::parse("{ test: { other: ':3', some: 123 }}"); + let ops = Value::parse("[{ op: 'move', path: '/temp', from: '/test/other' }]"); + let res = Value::parse("{ test: { some: 123 }, temp: ':3' }"); + val.patch(ops).unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn patch_test_embedded() { + let mut val = Value::parse("{ test: { other: 'test', something: 123 }, temp: true }"); + let ops = Value::parse("[{ op: 'remove', path: '/test/other' }, { op: 'test', path: '/test/something', value: 123 }]"); + let res = Value::parse("{ test: { something: 123 }, temp: true }"); + val.patch(ops).unwrap(); + assert_eq!(res, val); + } + + #[tokio::test] + async fn patch_change_invalid() { + // See https://github.com/surrealdb/surrealdb/issues/2001 + let mut val = Value::parse("{ test: { other: 'test', something: 123 }, temp: true }"); + let ops = Value::parse("[{ op: 'change', path: '/test/other', value: 'text' }]"); + assert!(val.patch(ops).is_err()); + } + + #[tokio::test] + async fn patch_test_invalid() { + let mut val = Value::parse("{ test: { other: 'test', something: 123 }, temp: true }"); + let should = val.clone(); + let ops = Value::parse("[{ op: 'remove', path: '/test/other' }, { op: 'test', path: '/test/something', value: 'not same' }]"); + assert!(val.patch(ops).is_err()); + // It is important to test if patches applied even if test operation fails + assert_eq!(val, should); + } +} diff --git a/core/src/sql/v2/value/pick.rs b/core/src/sql/v2/value/pick.rs new file mode 100644 index 00000000..79cfb2e1 --- /dev/null +++ b/core/src/sql/v2/value/pick.rs @@ -0,0 +1,134 @@ +use crate::sql::part::Next; +use crate::sql::part::Part; +use crate::sql::value::Value; + +impl Value { + /// Synchronous method for getting a field from a `Value` + pub fn pick(&self, path: &[Part]) -> Self { + match path.first() { + // Get the current value at path + Some(p) => match self { + // Current value at path is an object + Value::Object(v) => match p { + Part::Field(f) => match v.get(f as &str) { + Some(v) => v.pick(path.next()), + None => Value::None, + }, + Part::Index(i) => match v.get(&i.to_string()) { + Some(v) => v.pick(path.next()), + None => Value::None, + }, + Part::All => self.pick(path.next()), + _ => Value::None, + }, + // Current value at path is an array + Value::Array(v) => match p { + Part::All => v.iter().map(|v| v.pick(path.next())).collect::>().into(), + Part::First => match v.first() { + Some(v) => v.pick(path.next()), + None => Value::None, + }, + Part::Last => match v.last() { + Some(v) => v.pick(path.next()), + None => Value::None, + }, + Part::Index(i) => match v.get(i.to_usize()) { + Some(v) => v.pick(path.next()), + None => Value::None, + }, + _ => v.iter().map(|v| v.pick(path)).collect::>().into(), + }, + // Ignore everything else + _ => Value::None, + }, + // No more parts so get the value + None => self.clone(), + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::sql::id::Id; + use crate::sql::idiom::Idiom; + use crate::sql::thing::Thing; + use crate::syn::Parse; + + #[test] + fn pick_none() { + let idi = Idiom::default(); + let val = Value::parse("{ test: { other: null, something: 123 } }"); + let res = val.pick(&idi); + assert_eq!(res, val); + } + + #[test] + fn pick_basic() { + let idi = Idiom::parse("test.something"); + let val = Value::parse("{ test: { other: null, something: 123 } }"); + let res = val.pick(&idi); + assert_eq!(res, Value::from(123)); + } + + #[test] + fn pick_thing() { + let idi = Idiom::parse("test.other"); + let val = Value::parse("{ test: { other: test:tobie, something: 123 } }"); + let res = val.pick(&idi); + assert_eq!( + res, + Value::from(Thing { + tb: String::from("test"), + id: Id::from("tobie") + }) + ); + } + + #[test] + fn pick_array() { + let idi = Idiom::parse("test.something[1]"); + let val = Value::parse("{ test: { something: [123, 456, 789] } }"); + let res = val.pick(&idi); + assert_eq!(res, Value::from(456)); + } + + #[test] + fn pick_array_thing() { + let idi = Idiom::parse("test.something[1]"); + let val = Value::parse("{ test: { something: [test:tobie, test:jaime] } }"); + let res = val.pick(&idi); + assert_eq!( + res, + Value::from(Thing { + tb: String::from("test"), + id: Id::from("jaime") + }) + ); + } + + #[test] + fn pick_array_field() { + let idi = Idiom::parse("test.something[1].age"); + let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }"); + let res = val.pick(&idi); + assert_eq!(res, Value::from(36)); + } + + #[test] + fn pick_array_fields() { + let idi = Idiom::parse("test.something[*].age"); + let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }"); + let res = val.pick(&idi); + assert_eq!(res, Value::from(vec![34, 36])); + } + + #[test] + fn pick_array_fields_flat() { + let idi = Idiom::parse("test.something.age"); + let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }"); + let res = val.pick(&idi); + assert_eq!(res, Value::from(vec![34, 36])); + } +} diff --git a/core/src/sql/v2/value/put.rs b/core/src/sql/v2/value/put.rs new file mode 100644 index 00000000..a62199f9 --- /dev/null +++ b/core/src/sql/v2/value/put.rs @@ -0,0 +1,199 @@ +use crate::sql::part::Next; +use crate::sql::part::Part; +use crate::sql::value::Value; + +impl Value { + /// Synchronous method for setting a field on a `Value` + pub fn put(&mut self, path: &[Part], val: Value) { + match path.first() { + // Get the current value at path + Some(p) => match self { + // Current value at path is an object + Value::Object(v) => match p { + Part::Graph(g) => match v.get_mut(g.to_raw().as_str()) { + Some(v) if v.is_some() => v.put(path.next(), val), + _ => { + let mut obj = Value::base(); + obj.put(path.next(), val); + v.insert(g.to_raw(), obj); + } + }, + Part::Field(f) => match v.get_mut(f.to_raw().as_str()) { + Some(v) if v.is_some() => v.put(path.next(), val), + _ => { + let mut obj = Value::base(); + obj.put(path.next(), val); + v.insert(f.to_raw(), obj); + } + }, + Part::Index(i) => match v.get_mut(&i.to_string()) { + Some(v) if v.is_some() => v.put(path.next(), val), + _ => { + let mut obj = Value::base(); + obj.put(path.next(), val); + v.insert(i.to_string(), obj); + } + }, + _ => (), + }, + // Current value at path is an array + Value::Array(v) => match p { + Part::All => { + let path = path.next(); + v.iter_mut().for_each(|v| v.put(path, val.clone())); + } + Part::First => { + if let Some(v) = v.first_mut() { + v.put(path.next(), val) + } + } + Part::Last => { + if let Some(v) = v.last_mut() { + v.put(path.next(), val) + } + } + Part::Index(i) => { + if let Some(v) = v.get_mut(i.to_usize()) { + v.put(path.next(), val) + } + } + _ => { + v.iter_mut().for_each(|v| v.put(path, val.clone())); + } + }, + // Current value at path is empty + Value::Null => { + *self = Value::base(); + self.put(path, val) + } + // Current value at path is empty + Value::None => { + *self = Value::base(); + self.put(path, val) + } + // Ignore everything else + _ => (), + }, + // No more parts so put the value + None => { + *self = val; + } + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::sql::idiom::Idiom; + use crate::syn::Parse; + + #[tokio::test] + async fn put_none() { + let idi = Idiom::default(); + let mut val = Value::parse("{ test: { other: null, something: 123 } }"); + let res = Value::parse("999"); + val.put(&idi, Value::from(999)); + assert_eq!(res, val); + } + + #[tokio::test] + async fn put_empty() { + let idi = Idiom::parse("test"); + let mut val = Value::None; + let res = Value::parse("{ test: 999 }"); + val.put(&idi, Value::from(999)); + assert_eq!(res, val); + } + + #[tokio::test] + async fn put_blank() { + let idi = Idiom::parse("test.something"); + let mut val = Value::None; + let res = Value::parse("{ test: { something: 999 } }"); + val.put(&idi, Value::from(999)); + assert_eq!(res, val); + } + + #[tokio::test] + async fn put_reput() { + let idi = Idiom::parse("test"); + let mut val = Value::parse("{ test: { other: null, something: 123 } }"); + let res = Value::parse("{ test: 999 }"); + val.put(&idi, Value::from(999)); + assert_eq!(res, val); + } + + #[tokio::test] + async fn put_basic() { + let idi = Idiom::parse("test.something"); + let mut val = Value::parse("{ test: { other: null, something: 123 } }"); + let res = Value::parse("{ test: { other: null, something: 999 } }"); + val.put(&idi, Value::from(999)); + assert_eq!(res, val); + } + + #[tokio::test] + async fn put_allow() { + let idi = Idiom::parse("test.something.allow"); + let mut val = Value::parse("{ test: { other: null } }"); + let res = Value::parse("{ test: { other: null, something: { allow: 999 } } }"); + val.put(&idi, Value::from(999)); + assert_eq!(res, val); + } + + #[tokio::test] + async fn put_wrong() { + let idi = Idiom::parse("test.something.wrong"); + let mut val = Value::parse("{ test: { other: null, something: 123 } }"); + let res = Value::parse("{ test: { other: null, something: 123 } }"); + val.put(&idi, Value::from(999)); + assert_eq!(res, val); + } + + #[tokio::test] + async fn put_other() { + let idi = Idiom::parse("test.other.something"); + let mut val = Value::parse("{ test: { other: null, something: 123 } }"); + let res = Value::parse("{ test: { other: { something: 999 }, something: 123 } }"); + val.put(&idi, Value::from(999)); + assert_eq!(res, val); + } + + #[tokio::test] + async fn put_array() { + let idi = Idiom::parse("test.something[1]"); + let mut val = Value::parse("{ test: { something: [123, 456, 789] } }"); + let res = Value::parse("{ test: { something: [123, 999, 789] } }"); + val.put(&idi, Value::from(999)); + assert_eq!(res, val); + } + + #[tokio::test] + async fn put_array_field() { + let idi = Idiom::parse("test.something[1].age"); + let mut val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }"); + let res = Value::parse("{ test: { something: [{ age: 34 }, { age: 21 }] } }"); + val.put(&idi, Value::from(21)); + assert_eq!(res, val); + } + + #[tokio::test] + async fn put_array_fields() { + let idi = Idiom::parse("test.something[*].age"); + let mut val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }"); + let res = Value::parse("{ test: { something: [{ age: 21 }, { age: 21 }] } }"); + val.put(&idi, Value::from(21)); + assert_eq!(res, val); + } + + #[tokio::test] + async fn put_array_fields_flat() { + let idi = Idiom::parse("test.something.age"); + let mut val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }"); + let res = Value::parse("{ test: { something: [{ age: 21 }, { age: 21 }] } }"); + val.put(&idi, Value::from(21)); + assert_eq!(res, val); + } +} diff --git a/core/src/sql/v2/value/replace.rs b/core/src/sql/v2/value/replace.rs new file mode 100644 index 00000000..7dc39762 --- /dev/null +++ b/core/src/sql/v2/value/replace.rs @@ -0,0 +1,32 @@ +use crate::err::Error; +use crate::sql::value::Value; + +impl Value { + pub(crate) fn replace(&mut self, val: Value) -> Result<(), Error> { + // If this value is not an object, then error + if !val.is_object() { + return Err(Error::InvalidContent { + value: val, + }); + } + // Otherwise replace the current value + *self = val; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::syn::Parse; + + #[tokio::test] + async fn replace() { + let mut val = Value::parse("{ test: { other: null, something: 123 } }"); + let res = Value::parse("{ other: true }"); + let obj = Value::parse("{ other: true }"); + val.replace(obj).unwrap(); + assert_eq!(res, val); + } +} diff --git a/core/src/sql/v2/value/rid.rs b/core/src/sql/v2/value/rid.rs new file mode 100644 index 00000000..545e7f2c --- /dev/null +++ b/core/src/sql/v2/value/rid.rs @@ -0,0 +1,34 @@ +use crate::sql::paths::ID; +use crate::sql::value::Value; + +impl Value { + pub fn rid(&self) -> Value { + self.pick(&*ID) + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::sql::id::Id; + use crate::sql::thing::Thing; + use crate::syn::Parse; + + #[tokio::test] + async fn rid_none() { + let val = Value::parse("{ test: { other: null, something: 123 } }"); + let res = Value::None; + assert_eq!(res, val.rid()); + } + + #[tokio::test] + async fn rid_some() { + let val = Value::parse("{ id: test:id, test: { other: null, something: 123 } }"); + let res = Value::Thing(Thing { + tb: String::from("test"), + id: Id::from("id"), + }); + assert_eq!(res, val.rid()); + } +} diff --git a/core/src/sql/v2/value/serde/de/mod.rs b/core/src/sql/v2/value/serde/de/mod.rs new file mode 100644 index 00000000..17a675ee --- /dev/null +++ b/core/src/sql/v2/value/serde/de/mod.rs @@ -0,0 +1,698 @@ +use crate::sql::constant::ConstantValue; +use crate::sql::id::Gen; +use crate::sql::Value; +use serde::de::DeserializeOwned; +use serde::Serialize; +use serde_json::json; +use serde_json::Map; +use serde_json::Value as JsonValue; + +impl From for serde_json::Value { + fn from(value: Value) -> Self { + into_json(value, true) + } +} + +fn into_json(value: Value, simplify: bool) -> JsonValue { + use crate::sql; + use crate::sql::Number; + + #[derive(Serialize)] + struct Array(Vec); + + impl From<(sql::Array, bool)> for Array { + fn from((arr, simplify): (sql::Array, bool)) -> Self { + let mut vec = Vec::with_capacity(arr.0.len()); + for value in arr.0 { + vec.push(into_json(value, simplify)); + } + Self(vec) + } + } + + #[derive(Serialize)] + struct Object(Map); + + impl From<(sql::Object, bool)> for Object { + fn from((obj, simplify): (sql::Object, bool)) -> Self { + let mut map = Map::with_capacity(obj.0.len()); + for (key, value) in obj.0 { + map.insert(key.to_owned(), into_json(value, simplify)); + } + Self(map) + } + } + + #[derive(Serialize)] + enum CoordinatesType { + Point, + LineString, + Polygon, + MultiPoint, + MultiLineString, + MultiPolygon, + } + + #[derive(Serialize)] + struct Coordinates { + #[serde(rename = "type")] + typ: CoordinatesType, + coordinates: JsonValue, + } + + struct GeometryCollection; + + impl Serialize for GeometryCollection { + fn serialize(&self, s: S) -> Result + where + S: serde::Serializer, + { + s.serialize_str("GeometryCollection") + } + } + + #[derive(Serialize)] + struct Geometries { + #[serde(rename = "type")] + typ: GeometryCollection, + geometries: Vec, + } + + #[derive(Serialize)] + struct Geometry(JsonValue); + + impl From for Geometry { + fn from(geo: sql::Geometry) -> Self { + Self(match geo { + sql::Geometry::Point(v) => json!(Coordinates { + typ: CoordinatesType::Point, + coordinates: vec![json!(v.x()), json!(v.y())].into(), + }), + sql::Geometry::Line(v) => json!(Coordinates { + typ: CoordinatesType::LineString, + coordinates: v + .points() + .map(|p| vec![json!(p.x()), json!(p.y())].into()) + .collect::>() + .into(), + }), + sql::Geometry::Polygon(v) => json!(Coordinates { + typ: CoordinatesType::Polygon, + coordinates: vec![v + .exterior() + .points() + .map(|p| vec![json!(p.x()), json!(p.y())].into()) + .collect::>()] + .into_iter() + .chain( + v.interiors() + .iter() + .map(|i| { + i.points() + .map(|p| vec![json!(p.x()), json!(p.y())].into()) + .collect::>() + }) + .collect::>>(), + ) + .collect::>>() + .into(), + }), + sql::Geometry::MultiPoint(v) => json!(Coordinates { + typ: CoordinatesType::MultiPoint, + coordinates: v + .0 + .iter() + .map(|v| vec![json!(v.x()), json!(v.y())].into()) + .collect::>() + .into() + }), + sql::Geometry::MultiLine(v) => json!(Coordinates { + typ: CoordinatesType::MultiLineString, + coordinates: v + .0 + .iter() + .map(|v| { + v.points() + .map(|v| vec![json!(v.x()), json!(v.y())].into()) + .collect::>() + }) + .collect::>>() + .into() + }), + sql::Geometry::MultiPolygon(v) => json!(Coordinates { + typ: CoordinatesType::MultiPolygon, + coordinates: v + .0 + .iter() + .map(|v| { + vec![v + .exterior() + .points() + .map(|p| vec![json!(p.x()), json!(p.y())].into()) + .collect::>()] + .into_iter() + .chain( + v.interiors() + .iter() + .map(|i| { + i.points() + .map(|p| vec![json!(p.x()), json!(p.y())].into()) + .collect::>() + }) + .collect::>>(), + ) + .collect::>>() + }) + .collect::>>>() + .into(), + }), + sql::Geometry::Collection(v) => json!(Geometries { + typ: GeometryCollection, + geometries: v.into_iter().map(Geometry::from).map(|x| x.0).collect(), + }), + }) + } + } + + #[derive(Serialize)] + enum Id { + Number(i64), + String(String), + Array(Array), + Object(Object), + } + + impl From<(sql::Id, bool)> for Id { + fn from((id, simplify): (sql::Id, bool)) -> Self { + match id { + sql::Id::Number(n) => Id::Number(n), + sql::Id::String(s) => Id::String(s), + sql::Id::Array(arr) => Id::Array((arr, simplify).into()), + sql::Id::Object(obj) => Id::Object((obj, simplify).into()), + sql::Id::Generate(v) => match v { + Gen::Rand => Id::from((sql::Id::rand(), simplify)), + Gen::Ulid => Id::from((sql::Id::ulid(), simplify)), + Gen::Uuid => Id::from((sql::Id::uuid(), simplify)), + }, + } + } + } + + #[derive(Serialize)] + struct Thing { + tb: String, + id: Id, + } + + impl From<(sql::Thing, bool)> for Thing { + fn from((thing, simplify): (sql::Thing, bool)) -> Self { + Self { + tb: thing.tb, + id: (thing.id, simplify).into(), + } + } + } + + match value { + // These value types are simple values which + // can be used in query responses sent to + // the client. + Value::None | Value::Null => JsonValue::Null, + Value::Bool(boolean) => boolean.into(), + Value::Number(number) => match number { + Number::Int(int) => int.into(), + Number::Float(float) => float.into(), + Number::Decimal(decimal) => json!(decimal), + }, + Value::Strand(strand) => strand.0.into(), + Value::Duration(duration) => match simplify { + true => duration.to_raw().into(), + false => json!(duration.0), + }, + Value::Datetime(datetime) => json!(datetime.0), + Value::Uuid(uuid) => json!(uuid.0), + Value::Array(array) => JsonValue::Array(Array::from((array, simplify)).0), + Value::Object(object) => JsonValue::Object(Object::from((object, simplify)).0), + Value::Geometry(geo) => match simplify { + true => Geometry::from(geo).0, + false => match geo { + sql::Geometry::Point(geo) => json!(geo), + sql::Geometry::Line(geo) => json!(geo), + sql::Geometry::Polygon(geo) => json!(geo), + sql::Geometry::MultiPoint(geo) => json!(geo), + sql::Geometry::MultiLine(geo) => json!(geo), + sql::Geometry::MultiPolygon(geo) => json!(geo), + sql::Geometry::Collection(geo) => json!(geo), + }, + }, + Value::Bytes(bytes) => json!(bytes.0), + Value::Thing(thing) => match simplify { + true => thing.to_string().into(), + false => json!(thing), + }, + // These Value types are un-computed values + // and are not used in query responses sent + // to the client. + Value::Param(param) => json!(param), + Value::Idiom(idiom) => json!(idiom), + Value::Table(table) => json!(table), + Value::Mock(mock) => json!(mock), + Value::Regex(regex) => json!(regex), + Value::Block(block) => json!(block), + Value::Range(range) => json!(range), + Value::Edges(edges) => json!(edges), + Value::Future(future) => json!(future), + Value::Constant(constant) => match simplify { + true => match constant.value() { + ConstantValue::Datetime(datetime) => json!(datetime.0), + ConstantValue::Float(float) => float.into(), + }, + false => json!(constant), + }, + Value::Cast(cast) => json!(cast), + Value::Function(function) => json!(function), + Value::Model(model) => json!(model), + Value::Query(query) => json!(query), + Value::Subquery(subquery) => json!(subquery), + Value::Expression(expression) => json!(expression), + } +} + +#[derive(Debug, Clone)] +#[doc(hidden)] +#[non_exhaustive] +pub struct FromValueError { + pub value: Value, + pub error: String, +} + +/// Deserializes a value `T` from `SurrealDB` [`Value`] +#[doc(hidden)] +pub fn from_value(value: Value) -> Result +where + T: DeserializeOwned, +{ + let json = into_json(value.clone(), false); + serde_json::from_value(json).map_err(|error| FromValueError { + value, + error: error.to_string(), + }) +} + +#[cfg(test)] +mod tests { + mod into_json { + use crate::sql; + use crate::sql::value::serde::de::from_value; + use crate::sql::value::serde::de::into_json; + use crate::sql::Value; + use chrono::DateTime; + use chrono::Utc; + use geo::line_string; + use geo::point; + use geo::polygon; + use geo::LineString; + use geo::MultiLineString; + use geo::MultiPoint; + use geo::MultiPolygon; + use geo::Point; + use geo::Polygon; + use rust_decimal::Decimal; + use serde_json::json; + use std::collections::BTreeMap; + use std::time::Duration; + use uuid::Uuid; + + #[test] + fn none_or_null() { + for value in [Value::None, Value::Null] { + let simple_json = into_json(value.clone(), true); + assert_eq!(simple_json, json!(null)); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(null)); + + let response: Option = from_value(value).unwrap(); + assert_eq!(response, None); + } + } + + #[test] + fn bool() { + for boolean in [true, false] { + let value = Value::Bool(boolean); + + let simple_json = into_json(value.clone(), true); + assert_eq!(simple_json, json!(boolean)); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(boolean)); + + let response: bool = from_value(value).unwrap(); + assert_eq!(response, boolean); + } + } + + #[test] + fn number_int() { + for num in [i64::MIN, 0, i64::MAX] { + let value = Value::Number(sql::Number::Int(num)); + + let simple_json = into_json(value.clone(), true); + assert_eq!(simple_json, json!(num)); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(num)); + + let response: i64 = from_value(value).unwrap(); + assert_eq!(response, num); + } + } + + #[test] + fn number_float() { + for num in [f64::NEG_INFINITY, f64::MIN, 0.0, f64::MAX, f64::INFINITY, f64::NAN] { + let value = Value::Number(sql::Number::Float(num)); + + let simple_json = into_json(value.clone(), true); + assert_eq!(simple_json, json!(num)); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(num)); + + if num.is_finite() { + let response: f64 = from_value(value).unwrap(); + assert_eq!(response, num); + } else { + let response: Option = from_value(value).unwrap(); + assert_eq!(response, None); + } + } + } + + #[test] + fn number_decimal() { + for num in [i64::MIN, 0, i64::MAX] { + let num = Decimal::new(num, 0); + let value = Value::Number(sql::Number::Decimal(num)); + + let simple_json = into_json(value.clone(), true); + assert_eq!(simple_json, json!(num.to_string())); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(num)); + + let response: Decimal = from_value(value).unwrap(); + assert_eq!(response, num); + } + } + + #[test] + fn strand() { + for str in ["", "foo"] { + let value = Value::Strand(str.into()); + + let simple_json = into_json(value.clone(), true); + assert_eq!(simple_json, json!(str)); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(str)); + + let response: String = from_value(value).unwrap(); + assert_eq!(response, str); + } + } + + #[test] + fn duration() { + for duration in [Duration::ZERO, Duration::MAX] { + let value = Value::Duration(duration.into()); + + let simple_json = into_json(value.clone(), true); + assert_eq!(simple_json, json!(sql::Duration(duration).to_raw())); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(duration)); + + let response: Duration = from_value(value).unwrap(); + assert_eq!(response, duration); + } + } + + #[test] + fn datetime() { + for datetime in [DateTime::::MIN_UTC, DateTime::::MAX_UTC] { + let value = Value::Datetime(datetime.into()); + + let simple_json = into_json(value.clone(), true); + assert_eq!(simple_json, json!(datetime)); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(datetime)); + + let response: DateTime = from_value(value).unwrap(); + assert_eq!(response, datetime); + } + } + + #[test] + fn uuid() { + for uuid in [Uuid::nil(), Uuid::max()] { + let value = Value::Uuid(uuid.into()); + + let simple_json = into_json(value.clone(), true); + assert_eq!(simple_json, json!(uuid)); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(uuid)); + + let response: Uuid = from_value(value).unwrap(); + assert_eq!(response, uuid); + } + } + + #[test] + fn array() { + for vec in [vec![], vec![true, false]] { + let value = + Value::Array(sql::Array(vec.iter().copied().map(Value::from).collect())); + + let simple_json = into_json(value.clone(), true); + assert_eq!(simple_json, json!(vec)); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(vec)); + + let response: Vec = from_value(value).unwrap(); + assert_eq!(response, vec); + } + } + + #[test] + fn object() { + for map in [BTreeMap::new(), map!("done".to_owned() => true)] { + let value = Value::Object(sql::Object( + map.iter().map(|(key, value)| (key.clone(), Value::from(*value))).collect(), + )); + + let simple_json = into_json(value.clone(), true); + assert_eq!(simple_json, json!(map)); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(map)); + + let response: BTreeMap = from_value(value).unwrap(); + assert_eq!(response, map); + } + } + + #[test] + fn geometry_point() { + let point = point! { x: 10., y: 20. }; + let value = Value::Geometry(sql::Geometry::Point(point)); + + let simple_json = into_json(value.clone(), true); + assert_eq!(simple_json, json!({ "type": "Point", "coordinates": [10., 20.]})); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(point)); + + let response: Point = from_value(value).unwrap(); + assert_eq!(response, point); + } + + #[test] + fn geometry_line() { + let line_string = line_string![ + ( x: 0., y: 0. ), + ( x: 10., y: 0. ), + ]; + let value = Value::Geometry(sql::Geometry::Line(line_string.clone())); + + let simple_json = into_json(value.clone(), true); + assert_eq!( + simple_json, + json!({ "type": "LineString", "coordinates": [[0., 0.], [10., 0.]]}) + ); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(line_string)); + + let response: LineString = from_value(value).unwrap(); + assert_eq!(response, line_string); + } + + #[test] + fn geometry_polygon() { + let polygon = polygon![ + (x: -111., y: 45.), + (x: -111., y: 41.), + (x: -104., y: 41.), + (x: -104., y: 45.), + ]; + let value = Value::Geometry(sql::Geometry::Polygon(polygon.clone())); + + let simple_json = into_json(value.clone(), true); + assert_eq!( + simple_json, + json!({ "type": "Polygon", "coordinates": [[ + [-111., 45.], + [-111., 41.], + [-104., 41.], + [-104., 45.], + [-111., 45.], + ]]}) + ); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(polygon)); + + let response: Polygon = from_value(value).unwrap(); + assert_eq!(response, polygon); + } + + #[test] + fn geometry_multi_point() { + let multi_point: MultiPoint = + vec![point! { x: 0., y: 0. }, point! { x: 1., y: 2. }].into(); + let value = Value::Geometry(sql::Geometry::MultiPoint(multi_point.clone())); + + let simple_json = into_json(value.clone(), true); + assert_eq!( + simple_json, + json!({ "type": "MultiPoint", "coordinates": [[0., 0.], [1., 2.]]}) + ); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(multi_point)); + + let response: MultiPoint = from_value(value).unwrap(); + assert_eq!(response, multi_point); + } + + #[test] + fn geometry_multi_line() { + let multi_line = MultiLineString::new(vec![line_string![ + ( x: 0., y: 0. ), + ( x: 1., y: 2. ), + ]]); + let value = Value::Geometry(sql::Geometry::MultiLine(multi_line.clone())); + + let simple_json = into_json(value.clone(), true); + assert_eq!( + simple_json, + json!({ "type": "MultiLineString", "coordinates": [[[0., 0.], [1., 2.]]]}) + ); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(multi_line)); + + let response: MultiLineString = from_value(value).unwrap(); + assert_eq!(response, multi_line); + } + + #[test] + fn geometry_multi_polygon() { + let multi_polygon: MultiPolygon = vec![polygon![ + (x: -111., y: 45.), + (x: -111., y: 41.), + (x: -104., y: 41.), + (x: -104., y: 45.), + ]] + .into(); + let value = Value::Geometry(sql::Geometry::MultiPolygon(multi_polygon.clone())); + + let simple_json = into_json(value.clone(), true); + assert_eq!( + simple_json, + json!({ "type": "MultiPolygon", "coordinates": [[[ + [-111., 45.], + [-111., 41.], + [-104., 41.], + [-104., 45.], + [-111., 45.], + ]]]}) + ); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(multi_polygon)); + + let response: MultiPolygon = from_value(value).unwrap(); + assert_eq!(response, multi_polygon); + } + + #[test] + fn geometry_collection() { + for geometries in [vec![], vec![sql::Geometry::Point(point! { x: 10., y: 20. })]] { + let value = Value::Geometry(geometries.clone().into()); + + let simple_json = into_json(value.clone(), true); + assert_eq!( + simple_json, + json!({ + "type": "GeometryCollection", + "geometries": geometries.clone().into_iter().map(|geo| into_json(Value::from(geo), true)).collect::>(), + }) + ); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(geometries)); + + let response: Vec = from_value(value).unwrap(); + assert_eq!(response, geometries); + } + } + + #[test] + fn bytes() { + for bytes in [vec![], b"foo".to_vec()] { + let value = Value::Bytes(sql::Bytes(bytes.clone())); + + let simple_json = into_json(value.clone(), true); + assert_eq!(simple_json, json!(bytes)); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(bytes)); + + let response: Vec = from_value(value).unwrap(); + assert_eq!(response, bytes); + } + } + + #[test] + fn thing() { + let record_id = "foo:bar"; + let thing = sql::thing(record_id).unwrap(); + let value = Value::Thing(thing.clone()); + + let simple_json = into_json(value.clone(), true); + assert_eq!(simple_json, json!(record_id)); + + let json = into_json(value.clone(), false); + assert_eq!(json, json!(thing)); + + let response: sql::Thing = from_value(value).unwrap(); + assert_eq!(response, thing); + } + } +} diff --git a/core/src/sql/v2/value/serde/mod.rs b/core/src/sql/v2/value/serde/mod.rs new file mode 100644 index 00000000..227280c9 --- /dev/null +++ b/core/src/sql/v2/value/serde/mod.rs @@ -0,0 +1,5 @@ +mod de; +mod ser; + +pub use de::{from_value, FromValueError}; +pub use ser::to_value; diff --git a/core/src/sql/v2/value/serde/ser/algorithm/mod.rs b/core/src/sql/v2/value/serde/ser/algorithm/mod.rs new file mode 100644 index 00000000..17e57820 --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/algorithm/mod.rs @@ -0,0 +1,145 @@ +use crate::err::Error; +use crate::sql::value::serde::ser; +use crate::sql::Algorithm; +use serde::ser::Error as _; +use serde::ser::Impossible; + +pub(super) struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = Algorithm; + type Error = Error; + + type SerializeSeq = Impossible; + type SerializeTuple = Impossible; + type SerializeTupleStruct = Impossible; + type SerializeTupleVariant = Impossible; + type SerializeMap = Impossible; + type SerializeStruct = Impossible; + type SerializeStructVariant = Impossible; + + const EXPECTED: &'static str = "an enum `Algorithm`"; + + #[inline] + fn serialize_unit_variant( + self, + name: &'static str, + _variant_index: u32, + variant: &'static str, + ) -> Result { + match variant { + "EdDSA" => Ok(Algorithm::EdDSA), + "Es256" => Ok(Algorithm::Es256), + "Es384" => Ok(Algorithm::Es384), + "Es512" => Ok(Algorithm::Es512), + "Hs256" => Ok(Algorithm::Hs256), + "Hs384" => Ok(Algorithm::Hs384), + "Hs512" => Ok(Algorithm::Hs512), + "Ps256" => Ok(Algorithm::Ps256), + "Ps384" => Ok(Algorithm::Ps384), + "Ps512" => Ok(Algorithm::Ps512), + "Rs256" => Ok(Algorithm::Rs256), + "Rs384" => Ok(Algorithm::Rs384), + "Rs512" => Ok(Algorithm::Rs512), + variant => Err(Error::custom(format!("unknown variant `{name}::{variant}`"))), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ser::Serializer as _; + use serde::Serialize; + + #[test] + fn ed_dsa() { + let algo = Algorithm::EdDSA; + let serialized = algo.serialize(Serializer.wrap()).unwrap(); + assert_eq!(algo, serialized); + } + + #[test] + fn es256() { + let algo = Algorithm::Es256; + let serialized: Algorithm = algo.serialize(Serializer.wrap()).unwrap(); + assert_eq!(algo, serialized); + } + + #[test] + fn es384() { + let algo = Algorithm::Es384; + let serialized = algo.serialize(Serializer.wrap()).unwrap(); + assert_eq!(algo, serialized); + } + + #[test] + fn es512() { + let algo = Algorithm::Es512; + let serialized = algo.serialize(Serializer.wrap()).unwrap(); + assert_eq!(algo, serialized); + } + + #[test] + fn hs256() { + let algo = Algorithm::Hs256; + let serialized = algo.serialize(Serializer.wrap()).unwrap(); + assert_eq!(algo, serialized); + } + + #[test] + fn hs384() { + let algo = Algorithm::Hs384; + let serialized = algo.serialize(Serializer.wrap()).unwrap(); + assert_eq!(algo, serialized); + } + + #[test] + fn hs512() { + let algo = Algorithm::Hs512; + let serialized = algo.serialize(Serializer.wrap()).unwrap(); + assert_eq!(algo, serialized); + } + + #[test] + fn ps256() { + let algo = Algorithm::Ps256; + let serialized = algo.serialize(Serializer.wrap()).unwrap(); + assert_eq!(algo, serialized); + } + + #[test] + fn ps384() { + let algo = Algorithm::Ps384; + let serialized = algo.serialize(Serializer.wrap()).unwrap(); + assert_eq!(algo, serialized); + } + + #[test] + fn ps512() { + let algo = Algorithm::Ps512; + let serialized = algo.serialize(Serializer.wrap()).unwrap(); + assert_eq!(algo, serialized); + } + + #[test] + fn rs256() { + let algo = Algorithm::Rs256; + let serialized = algo.serialize(Serializer.wrap()).unwrap(); + assert_eq!(algo, serialized); + } + + #[test] + fn rs384() { + let algo = Algorithm::Rs384; + let serialized = algo.serialize(Serializer.wrap()).unwrap(); + assert_eq!(algo, serialized); + } + + #[test] + fn rs512() { + let algo = Algorithm::Rs512; + let serialized = algo.serialize(Serializer.wrap()).unwrap(); + assert_eq!(algo, serialized); + } +} diff --git a/core/src/sql/v2/value/serde/ser/base/mod.rs b/core/src/sql/v2/value/serde/ser/base/mod.rs new file mode 100644 index 00000000..4efa0757 --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/base/mod.rs @@ -0,0 +1,94 @@ +pub(super) mod opt; + +use crate::err::Error; +use crate::sql::value::serde::ser; +use crate::sql::Base; +use crate::sql::Ident; +use serde::ser::Error as _; +use serde::ser::Impossible; +use serde::ser::Serialize; + +pub(super) struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = Base; + type Error = Error; + + type SerializeSeq = Impossible; + type SerializeTuple = Impossible; + type SerializeTupleStruct = Impossible; + type SerializeTupleVariant = Impossible; + type SerializeMap = Impossible; + type SerializeStruct = Impossible; + type SerializeStructVariant = Impossible; + + const EXPECTED: &'static str = "an enum `Base`"; + + #[inline] + fn serialize_unit_variant( + self, + name: &'static str, + _variant_index: u32, + variant: &'static str, + ) -> Result { + match variant { + "Root" => Ok(Base::Root), + "Ns" => Ok(Base::Ns), + "Db" => Ok(Base::Db), + variant => Err(Error::custom(format!("unexpected unit variant `{name}::{variant}`"))), + } + } + + #[inline] + fn serialize_newtype_variant( + self, + name: &'static str, + _variant_index: u32, + variant: &'static str, + value: &T, + ) -> Result + where + T: ?Sized + Serialize, + { + match variant { + "Sc" => Ok(Base::Sc(Ident(value.serialize(ser::string::Serializer.wrap())?))), + variant => { + Err(Error::custom(format!("unexpected newtype variant `{name}::{variant}`"))) + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ser::Serializer as _; + + #[test] + fn root() { + let base = Base::Root; + let serialized = base.serialize(Serializer.wrap()).unwrap(); + assert_eq!(base, serialized); + } + + #[test] + fn ns() { + let base = Base::Ns; + let serialized = base.serialize(Serializer.wrap()).unwrap(); + assert_eq!(base, serialized); + } + + #[test] + fn db() { + let base = Base::Db; + let serialized = base.serialize(Serializer.wrap()).unwrap(); + assert_eq!(base, serialized); + } + + #[test] + fn sc() { + let base = Base::Sc(Default::default()); + let serialized = base.serialize(Serializer.wrap()).unwrap(); + assert_eq!(base, serialized); + } +} diff --git a/core/src/sql/v2/value/serde/ser/base/opt.rs b/core/src/sql/v2/value/serde/ser/base/opt.rs new file mode 100644 index 00000000..10ddefbb --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/base/opt.rs @@ -0,0 +1,55 @@ +use crate::err::Error; +use crate::sql::value::serde::ser; +use crate::sql::Base; +use serde::ser::Impossible; +use serde::ser::Serialize; + +pub struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = Option; + type Error = Error; + + type SerializeSeq = Impossible, Error>; + type SerializeTuple = Impossible, Error>; + type SerializeTupleStruct = Impossible, Error>; + type SerializeTupleVariant = Impossible, Error>; + type SerializeMap = Impossible, Error>; + type SerializeStruct = Impossible, Error>; + type SerializeStructVariant = Impossible, Error>; + + const EXPECTED: &'static str = "an `Option`"; + + #[inline] + fn serialize_none(self) -> Result { + Ok(None) + } + + #[inline] + fn serialize_some(self, value: &T) -> Result + where + T: ?Sized + Serialize, + { + Ok(Some(value.serialize(super::Serializer.wrap())?)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ser::Serializer as _; + + #[test] + fn none() { + let option: Option = None; + let serialized = option.serialize(Serializer.wrap()).unwrap(); + assert_eq!(option, serialized); + } + + #[test] + fn some() { + let option = Some(Base::default()); + let serialized = option.serialize(Serializer.wrap()).unwrap(); + assert_eq!(option, serialized); + } +} diff --git a/core/src/sql/v2/value/serde/ser/block/entry/mod.rs b/core/src/sql/v2/value/serde/ser/block/entry/mod.rs new file mode 100644 index 00000000..7335e721 --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/block/entry/mod.rs @@ -0,0 +1,157 @@ +pub mod vec; + +use crate::err::Error; +use crate::sql::block::Entry; +use crate::sql::value::serde::ser; +use serde::ser::Error as _; +use serde::ser::Impossible; +use serde::ser::Serialize; + +pub(super) struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = Entry; + type Error = Error; + + type SerializeSeq = Impossible; + type SerializeTuple = Impossible; + type SerializeTupleStruct = Impossible; + type SerializeTupleVariant = Impossible; + type SerializeMap = Impossible; + type SerializeStruct = Impossible; + type SerializeStructVariant = Impossible; + + const EXPECTED: &'static str = "an enum `Entry`"; + + #[inline] + fn serialize_newtype_variant( + self, + name: &'static str, + _variant_index: u32, + variant: &'static str, + value: &T, + ) -> Result + where + T: ?Sized + Serialize, + { + match variant { + "Value" => Ok(Entry::Value(value.serialize(ser::value::Serializer.wrap())?)), + "Set" => Ok(Entry::Set(value.serialize(ser::statement::set::Serializer.wrap())?)), + "Throw" => Ok(Entry::Throw(value.serialize(ser::statement::throw::Serializer.wrap())?)), + "Break" => { + Ok(Entry::Break(value.serialize(ser::statement::r#break::Serializer.wrap())?)) + } + "Ifelse" => { + Ok(Entry::Ifelse(value.serialize(ser::statement::ifelse::Serializer.wrap())?)) + } + "Select" => { + Ok(Entry::Select(value.serialize(ser::statement::select::Serializer.wrap())?)) + } + "Create" => { + Ok(Entry::Create(value.serialize(ser::statement::create::Serializer.wrap())?)) + } + "Update" => { + Ok(Entry::Update(value.serialize(ser::statement::update::Serializer.wrap())?)) + } + "Delete" => { + Ok(Entry::Delete(value.serialize(ser::statement::delete::Serializer.wrap())?)) + } + "Relate" => { + Ok(Entry::Relate(value.serialize(ser::statement::relate::Serializer.wrap())?)) + } + "Insert" => { + Ok(Entry::Insert(value.serialize(ser::statement::insert::Serializer.wrap())?)) + } + "Output" => { + Ok(Entry::Output(value.serialize(ser::statement::output::Serializer.wrap())?)) + } + "Define" => { + Ok(Entry::Define(value.serialize(ser::statement::define::Serializer.wrap())?)) + } + "Remove" => { + Ok(Entry::Remove(value.serialize(ser::statement::remove::Serializer.wrap())?)) + } + "Continue" => { + Ok(Entry::Continue(value.serialize(ser::statement::r#continue::Serializer.wrap())?)) + } + variant => Err(Error::custom(format!("unexpected variant `{name}::{variant}`"))), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ser::Serializer as _; + use serde::Serialize; + + #[test] + fn value() { + let entry = Entry::Value(Default::default()); + let serialized = entry.serialize(Serializer.wrap()).unwrap(); + assert_eq!(entry, serialized); + } + + #[test] + fn set() { + let entry = Entry::Set(Default::default()); + let serialized = entry.serialize(Serializer.wrap()).unwrap(); + assert_eq!(entry, serialized); + } + + #[test] + fn ifelse() { + let entry = Entry::Ifelse(Default::default()); + let serialized = entry.serialize(Serializer.wrap()).unwrap(); + assert_eq!(entry, serialized); + } + + #[test] + fn select() { + let entry = Entry::Select(Default::default()); + let serialized = entry.serialize(Serializer.wrap()).unwrap(); + assert_eq!(entry, serialized); + } + + #[test] + fn create() { + let entry = Entry::Create(Default::default()); + let serialized = entry.serialize(Serializer.wrap()).unwrap(); + assert_eq!(entry, serialized); + } + + #[test] + fn update() { + let entry = Entry::Update(Default::default()); + let serialized = entry.serialize(Serializer.wrap()).unwrap(); + assert_eq!(entry, serialized); + } + + #[test] + fn delete() { + let entry = Entry::Delete(Default::default()); + let serialized = entry.serialize(Serializer.wrap()).unwrap(); + assert_eq!(entry, serialized); + } + + #[test] + fn relate() { + let entry = Entry::Relate(Default::default()); + let serialized = entry.serialize(Serializer.wrap()).unwrap(); + assert_eq!(entry, serialized); + } + + #[test] + fn insert() { + let entry = Entry::Insert(Default::default()); + let serialized = entry.serialize(Serializer.wrap()).unwrap(); + assert_eq!(entry, serialized); + } + + #[test] + fn output() { + let entry = Entry::Output(Default::default()); + let serialized = entry.serialize(Serializer.wrap()).unwrap(); + assert_eq!(entry, serialized); + } +} diff --git a/core/src/sql/v2/value/serde/ser/block/entry/vec.rs b/core/src/sql/v2/value/serde/ser/block/entry/vec.rs new file mode 100644 index 00000000..3256ee74 --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/block/entry/vec.rs @@ -0,0 +1,77 @@ +use crate::err::Error; +use crate::sql::block::Entry; +use crate::sql::value::serde::ser; +use ser::Serializer as _; +use serde::ser::Impossible; +use serde::ser::Serialize; + +pub struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = Vec; + type Error = Error; + + type SerializeSeq = SerializeEntryVec; + type SerializeTuple = Impossible, Error>; + type SerializeTupleStruct = Impossible, Error>; + type SerializeTupleVariant = Impossible, Error>; + type SerializeMap = Impossible, Error>; + type SerializeStruct = Impossible, Error>; + type SerializeStructVariant = Impossible, Error>; + + const EXPECTED: &'static str = "a `Vec`"; + + fn serialize_seq(self, len: Option) -> Result { + Ok(SerializeEntryVec(Vec::with_capacity(len.unwrap_or_default()))) + } + + #[inline] + fn serialize_newtype_struct( + self, + _name: &'static str, + value: &T, + ) -> Result + where + T: ?Sized + Serialize, + { + value.serialize(self.wrap()) + } +} + +pub struct SerializeEntryVec(Vec); + +impl serde::ser::SerializeSeq for SerializeEntryVec { + type Ok = Vec; + type Error = Error; + + fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize + ?Sized, + { + self.0.push(value.serialize(super::Serializer.wrap())?); + Ok(()) + } + + fn end(self) -> Result { + Ok(self.0) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn empty() { + let vec: Vec = Vec::new(); + let serialized = vec.serialize(Serializer.wrap()).unwrap(); + assert_eq!(vec, serialized); + } + + #[test] + fn vec() { + let vec = vec![Entry::Value(Default::default())]; + let serialized = vec.serialize(Serializer.wrap()).unwrap(); + assert_eq!(vec, serialized); + } +} diff --git a/core/src/sql/v2/value/serde/ser/block/mod.rs b/core/src/sql/v2/value/serde/ser/block/mod.rs new file mode 100644 index 00000000..10d38b95 --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/block/mod.rs @@ -0,0 +1 @@ +pub(super) mod entry; diff --git a/core/src/sql/v2/value/serde/ser/cast/mod.rs b/core/src/sql/v2/value/serde/ser/cast/mod.rs new file mode 100644 index 00000000..2b8b52c3 --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/cast/mod.rs @@ -0,0 +1,85 @@ +use crate::err::Error; +use crate::sql::value::serde::ser; +use crate::sql::Cast; +use crate::sql::Kind; +use crate::sql::Value; +use ser::Serializer as _; +use serde::ser::Error as _; +use serde::ser::Impossible; +use serde::ser::Serialize; + +pub(super) struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = Cast; + type Error = Error; + + type SerializeSeq = Impossible; + type SerializeTuple = Impossible; + type SerializeTupleStruct = SerializeCast; + type SerializeTupleVariant = Impossible; + type SerializeMap = Impossible; + type SerializeStruct = Impossible; + type SerializeStructVariant = Impossible; + + const EXPECTED: &'static str = "an struct `Cast`"; + + fn serialize_tuple_struct( + self, + _name: &'static str, + _len: usize, + ) -> Result { + Ok(SerializeCast::default()) + } +} + +#[derive(Default)] +pub(super) struct SerializeCast { + index: usize, + kind: Option, + value: Option, +} + +impl serde::ser::SerializeTupleStruct for SerializeCast { + type Ok = Cast; + type Error = Error; + + fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize + ?Sized, + { + match self.index { + 0 => { + self.kind = Some(value.serialize(ser::kind::Serializer.wrap())?); + } + 1 => { + self.value = Some(value.serialize(ser::value::Serializer.wrap())?); + } + index => { + return Err(Error::custom(format!("unexpected `Cast` index `{index}`"))); + } + } + self.index += 1; + Ok(()) + } + + fn end(self) -> Result { + match (self.kind, self.value) { + (Some(kind), Some(value)) => Ok(Cast(kind, value)), + _ => Err(Error::custom("`Cast` missing required value(s)")), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde::Serialize; + + #[test] + fn cast() { + let cast = Cast(Default::default(), Default::default()); + let serialized = cast.serialize(Serializer.wrap()).unwrap(); + assert_eq!(cast, serialized); + } +} diff --git a/core/src/sql/v2/value/serde/ser/changefeed/mod.rs b/core/src/sql/v2/value/serde/ser/changefeed/mod.rs new file mode 100644 index 00000000..ca108d48 --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/changefeed/mod.rs @@ -0,0 +1,79 @@ +pub(super) mod opt; + +use crate::err::Error; +use crate::sql::changefeed::ChangeFeed; +use crate::sql::value::serde::ser; +use ser::Serializer as _; +use serde::ser::Error as _; +use serde::ser::Impossible; +use serde::ser::Serialize; +use std::time::Duration; + +pub struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = ChangeFeed; + type Error = Error; + + type SerializeSeq = Impossible; + type SerializeTuple = Impossible; + type SerializeTupleStruct = Impossible; + type SerializeTupleVariant = Impossible; + type SerializeMap = Impossible; + type SerializeStruct = SerializeChangeFeed; + type SerializeStructVariant = Impossible; + + const EXPECTED: &'static str = "a struct `ChangeFeed`"; + + #[inline] + fn serialize_struct( + self, + _name: &'static str, + _len: usize, + ) -> Result { + Ok(SerializeChangeFeed::default()) + } +} + +#[derive(Default)] +pub struct SerializeChangeFeed { + expiry: Duration, +} + +impl serde::ser::SerializeStruct for SerializeChangeFeed { + type Ok = ChangeFeed; + type Error = Error; + + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Error> + where + T: ?Sized + Serialize, + { + match key { + "expiry" => { + self.expiry = value.serialize(ser::duration::Serializer.wrap())?; + } + key => { + return Err(Error::custom(format!("unexpected field `ChangeFeed::{key}`"))); + } + } + Ok(()) + } + + fn end(self) -> Result { + Ok(ChangeFeed { + expiry: self.expiry, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn default() { + let stmt = ChangeFeed::default(); + let value: ChangeFeed = stmt.serialize(Serializer.wrap()).unwrap(); + assert_eq!(value, stmt); + } +} diff --git a/core/src/sql/v2/value/serde/ser/changefeed/opt.rs b/core/src/sql/v2/value/serde/ser/changefeed/opt.rs new file mode 100644 index 00000000..711e32f2 --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/changefeed/opt.rs @@ -0,0 +1,55 @@ +use crate::err::Error; +use crate::sql::changefeed::ChangeFeed; +use crate::sql::value::serde::ser; +use serde::ser::Impossible; +use serde::ser::Serialize; + +pub struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = Option; + type Error = Error; + + type SerializeSeq = Impossible, Error>; + type SerializeTuple = Impossible, Error>; + type SerializeTupleStruct = Impossible, Error>; + type SerializeTupleVariant = Impossible, Error>; + type SerializeMap = Impossible, Error>; + type SerializeStruct = Impossible, Error>; + type SerializeStructVariant = Impossible, Error>; + + const EXPECTED: &'static str = "an `Option`"; + + #[inline] + fn serialize_none(self) -> Result { + Ok(None) + } + + #[inline] + fn serialize_some(self, value: &T) -> Result + where + T: ?Sized + Serialize, + { + Ok(Some(value.serialize(super::Serializer.wrap())?)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ser::Serializer as _; + + #[test] + fn none() { + let option: Option = None; + let serialized = option.serialize(Serializer.wrap()).unwrap(); + assert_eq!(option, serialized); + } + + #[test] + fn some() { + let option = Some(ChangeFeed::default()); + let serialized = option.serialize(Serializer.wrap()).unwrap(); + assert_eq!(option, serialized); + } +} diff --git a/core/src/sql/v2/value/serde/ser/cond/mod.rs b/core/src/sql/v2/value/serde/ser/cond/mod.rs new file mode 100644 index 00000000..0db91d8e --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/cond/mod.rs @@ -0,0 +1 @@ +pub(super) mod opt; diff --git a/core/src/sql/v2/value/serde/ser/cond/opt.rs b/core/src/sql/v2/value/serde/ser/cond/opt.rs new file mode 100644 index 00000000..3ab454a5 --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/cond/opt.rs @@ -0,0 +1,55 @@ +use crate::err::Error; +use crate::sql::value::serde::ser; +use crate::sql::Cond; +use serde::ser::Impossible; +use serde::ser::Serialize; + +pub struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = Option; + type Error = Error; + + type SerializeSeq = Impossible, Error>; + type SerializeTuple = Impossible, Error>; + type SerializeTupleStruct = Impossible, Error>; + type SerializeTupleVariant = Impossible, Error>; + type SerializeMap = Impossible, Error>; + type SerializeStruct = Impossible, Error>; + type SerializeStructVariant = Impossible, Error>; + + const EXPECTED: &'static str = "an `Option`"; + + #[inline] + fn serialize_none(self) -> Result { + Ok(None) + } + + #[inline] + fn serialize_some(self, value: &T) -> Result + where + T: ?Sized + Serialize, + { + Ok(Some(Cond(value.serialize(ser::value::Serializer.wrap())?))) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ser::Serializer as _; + + #[test] + fn none() { + let option: Option = None; + let serialized = option.serialize(Serializer.wrap()).unwrap(); + assert_eq!(option, serialized); + } + + #[test] + fn some() { + let option = Some(Cond::default()); + let serialized = option.serialize(Serializer.wrap()).unwrap(); + assert_eq!(option, serialized); + } +} diff --git a/core/src/sql/v2/value/serde/ser/constant/mod.rs b/core/src/sql/v2/value/serde/ser/constant/mod.rs new file mode 100644 index 00000000..73d72f95 --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/constant/mod.rs @@ -0,0 +1,193 @@ +use crate::err::Error; +use crate::sql::constant::Constant; +use crate::sql::value::serde::ser; +use serde::ser::Error as _; +use serde::ser::Impossible; + +pub(super) struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = Constant; + type Error = Error; + + type SerializeSeq = Impossible; + type SerializeTuple = Impossible; + type SerializeTupleStruct = Impossible; + type SerializeTupleVariant = Impossible; + type SerializeMap = Impossible; + type SerializeStruct = Impossible; + type SerializeStructVariant = Impossible; + + const EXPECTED: &'static str = "an enum `Constant`"; + + #[inline] + fn serialize_unit_variant( + self, + name: &'static str, + _variant_index: u32, + variant: &'static str, + ) -> Result { + match variant { + "MathE" => Ok(Constant::MathE), + "MathFrac1Pi" => Ok(Constant::MathFrac1Pi), + "MathFrac1Sqrt2" => Ok(Constant::MathFrac1Sqrt2), + "MathFrac2Pi" => Ok(Constant::MathFrac2Pi), + "MathFrac2SqrtPi" => Ok(Constant::MathFrac2SqrtPi), + "MathFracPi2" => Ok(Constant::MathFracPi2), + "MathFracPi3" => Ok(Constant::MathFracPi3), + "MathFracPi4" => Ok(Constant::MathFracPi4), + "MathFracPi6" => Ok(Constant::MathFracPi6), + "MathFracPi8" => Ok(Constant::MathFracPi8), + "MathLn10" => Ok(Constant::MathLn10), + "MathLn2" => Ok(Constant::MathLn2), + "MathLog102" => Ok(Constant::MathLog102), + "MathLog10E" => Ok(Constant::MathLog10E), + "MathLog210" => Ok(Constant::MathLog210), + "MathLog2E" => Ok(Constant::MathLog2E), + "MathPi" => Ok(Constant::MathPi), + "MathSqrt2" => Ok(Constant::MathSqrt2), + "MathTau" => Ok(Constant::MathTau), + variant => Err(Error::custom(format!("unknown variant `{name}::{variant}`"))), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ser::Serializer as _; + use serde::Serialize; + + #[test] + fn math_e() { + let constant = Constant::MathE; + let serialized = constant.serialize(Serializer.wrap()).unwrap(); + assert_eq!(constant, serialized); + } + + #[test] + fn math_frac_1pi() { + let constant = Constant::MathFrac1Pi; + let serialized = constant.serialize(Serializer.wrap()).unwrap(); + assert_eq!(constant, serialized); + } + + #[test] + fn math_frac_1sqrt2() { + let constant = Constant::MathFrac1Sqrt2; + let serialized = constant.serialize(Serializer.wrap()).unwrap(); + assert_eq!(constant, serialized); + } + + #[test] + fn math_frac_2pi() { + let constant = Constant::MathFrac2Pi; + let serialized = constant.serialize(Serializer.wrap()).unwrap(); + assert_eq!(constant, serialized); + } + + #[test] + fn math_frac_2sqrt_pi() { + let constant = Constant::MathFrac2SqrtPi; + let serialized = constant.serialize(Serializer.wrap()).unwrap(); + assert_eq!(constant, serialized); + } + + #[test] + fn math_frac_pi2() { + let constant = Constant::MathFracPi2; + let serialized = constant.serialize(Serializer.wrap()).unwrap(); + assert_eq!(constant, serialized); + } + + #[test] + fn math_frac_pi3() { + let constant = Constant::MathFracPi3; + let serialized = constant.serialize(Serializer.wrap()).unwrap(); + assert_eq!(constant, serialized); + } + + #[test] + fn math_frac_pi4() { + let constant = Constant::MathFracPi4; + let serialized = constant.serialize(Serializer.wrap()).unwrap(); + assert_eq!(constant, serialized); + } + + #[test] + fn math_frac_pi6() { + let constant = Constant::MathFracPi6; + let serialized = constant.serialize(Serializer.wrap()).unwrap(); + assert_eq!(constant, serialized); + } + + #[test] + fn math_frac_pi8() { + let constant = Constant::MathFracPi8; + let serialized = constant.serialize(Serializer.wrap()).unwrap(); + assert_eq!(constant, serialized); + } + + #[test] + fn math_ln10() { + let constant = Constant::MathLn10; + let serialized = constant.serialize(Serializer.wrap()).unwrap(); + assert_eq!(constant, serialized); + } + + #[test] + fn math_ln2() { + let constant = Constant::MathLn2; + let serialized = constant.serialize(Serializer.wrap()).unwrap(); + assert_eq!(constant, serialized); + } + + #[test] + fn math_log102() { + let constant = Constant::MathLog102; + let serialized = constant.serialize(Serializer.wrap()).unwrap(); + assert_eq!(constant, serialized); + } + + #[test] + fn math_log10_e() { + let constant = Constant::MathLog10E; + let serialized = constant.serialize(Serializer.wrap()).unwrap(); + assert_eq!(constant, serialized); + } + + #[test] + fn math_log210() { + let constant = Constant::MathLog210; + let serialized = constant.serialize(Serializer.wrap()).unwrap(); + assert_eq!(constant, serialized); + } + + #[test] + fn math_log2_e() { + let constant = Constant::MathLog2E; + let serialized = constant.serialize(Serializer.wrap()).unwrap(); + assert_eq!(constant, serialized); + } + + #[test] + fn math_pi() { + let constant = Constant::MathPi; + let serialized = constant.serialize(Serializer.wrap()).unwrap(); + assert_eq!(constant, serialized); + } + + #[test] + fn math_sqrt2() { + let constant = Constant::MathSqrt2; + let serialized = constant.serialize(Serializer.wrap()).unwrap(); + assert_eq!(constant, serialized); + } + + #[test] + fn math_tau() { + let constant = Constant::MathTau; + let serialized = constant.serialize(Serializer.wrap()).unwrap(); + assert_eq!(constant, serialized); + } +} diff --git a/core/src/sql/v2/value/serde/ser/data/mod.rs b/core/src/sql/v2/value/serde/ser/data/mod.rs new file mode 100644 index 00000000..2a1d4548 --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/data/mod.rs @@ -0,0 +1,457 @@ +pub(super) mod opt; + +use crate::err::Error; +use crate::sql::value::serde::ser; +use crate::sql::Data; +use crate::sql::Idiom; +use crate::sql::Operator; +use crate::sql::Value; +use ser::Serializer as _; +use serde::ser::Error as _; +use serde::ser::Impossible; +use serde::ser::Serialize; + +pub(super) struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = Data; + type Error = Error; + + type SerializeSeq = Impossible; + type SerializeTuple = Impossible; + type SerializeTupleStruct = Impossible; + type SerializeTupleVariant = Impossible; + type SerializeMap = Impossible; + type SerializeStruct = Impossible; + type SerializeStructVariant = Impossible; + + const EXPECTED: &'static str = "an enum `Data`"; + + #[inline] + fn serialize_unit_variant( + self, + name: &'static str, + _variant_index: u32, + variant: &'static str, + ) -> Result { + match variant { + "EmptyExpression" => Ok(Data::EmptyExpression), + variant => Err(Error::custom(format!("unexpected unit variant `{name}::{variant}`"))), + } + } + + #[inline] + fn serialize_newtype_variant( + self, + name: &'static str, + _variant_index: u32, + variant: &'static str, + value: &T, + ) -> Result + where + T: ?Sized + Serialize, + { + match variant { + "SetExpression" => { + Ok(Data::SetExpression(value.serialize(IdiomOperatorValueVecSerializer.wrap())?)) + } + "UnsetExpression" => { + Ok(Data::UnsetExpression(value.serialize(IdiomVecSerializer.wrap())?)) + } + "PatchExpression" => { + Ok(Data::PatchExpression(value.serialize(ser::value::Serializer.wrap())?)) + } + "MergeExpression" => { + Ok(Data::MergeExpression(value.serialize(ser::value::Serializer.wrap())?)) + } + "ReplaceExpression" => { + Ok(Data::ReplaceExpression(value.serialize(ser::value::Serializer.wrap())?)) + } + "ContentExpression" => { + Ok(Data::ContentExpression(value.serialize(ser::value::Serializer.wrap())?)) + } + "SingleExpression" => { + Ok(Data::SingleExpression(value.serialize(ser::value::Serializer.wrap())?)) + } + "ValuesExpression" => { + Ok(Data::ValuesExpression(value.serialize(IdiomValueVecVecSerializer.wrap())?)) + } + "UpdateExpression" => { + Ok(Data::UpdateExpression(value.serialize(IdiomOperatorValueVecSerializer.wrap())?)) + } + variant => { + Err(Error::custom(format!("unexpected newtype variant `{name}::{variant}`"))) + } + } + } +} + +struct IdiomVecSerializer; + +impl ser::Serializer for IdiomVecSerializer { + type Ok = Vec; + type Error = Error; + + type SerializeSeq = SerializeIdiomVec; + type SerializeTuple = Impossible, Error>; + type SerializeTupleStruct = Impossible, Error>; + type SerializeTupleVariant = Impossible, Error>; + type SerializeMap = Impossible, Error>; + type SerializeStruct = Impossible, Error>; + type SerializeStructVariant = Impossible, Error>; + + const EXPECTED: &'static str = "an `Vec`"; + + fn serialize_seq(self, len: Option) -> Result { + Ok(SerializeIdiomVec(Vec::with_capacity(len.unwrap_or_default()))) + } +} + +struct SerializeIdiomVec(Vec); + +impl serde::ser::SerializeSeq for SerializeIdiomVec { + type Ok = Vec; + type Error = Error; + + fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize + ?Sized, + { + self.0.push(Idiom(value.serialize(ser::part::vec::Serializer.wrap())?)); + Ok(()) + } + + fn end(self) -> Result { + Ok(self.0) + } +} + +type IdiomOperatorValueTuple = (Idiom, Operator, Value); + +struct IdiomOperatorValueVecSerializer; + +impl ser::Serializer for IdiomOperatorValueVecSerializer { + type Ok = Vec; + type Error = Error; + + type SerializeSeq = SerializeIdiomOperatorValueVec; + type SerializeTuple = Impossible, Error>; + type SerializeTupleStruct = Impossible, Error>; + type SerializeTupleVariant = Impossible, Error>; + type SerializeMap = Impossible, Error>; + type SerializeStruct = Impossible, Error>; + type SerializeStructVariant = Impossible, Error>; + + const EXPECTED: &'static str = "an `(Idiom, Operator, Value)`"; + + fn serialize_seq(self, len: Option) -> Result { + Ok(SerializeIdiomOperatorValueVec(Vec::with_capacity(len.unwrap_or_default()))) + } +} + +struct SerializeIdiomOperatorValueVec(Vec); + +impl serde::ser::SerializeSeq for SerializeIdiomOperatorValueVec { + type Ok = Vec; + type Error = Error; + + fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize + ?Sized, + { + self.0.push(value.serialize(IdiomOperatorValueTupleSerializer.wrap())?); + Ok(()) + } + + fn end(self) -> Result { + Ok(self.0) + } +} + +struct IdiomOperatorValueTupleSerializer; + +impl ser::Serializer for IdiomOperatorValueTupleSerializer { + type Ok = IdiomOperatorValueTuple; + type Error = Error; + + type SerializeSeq = Impossible; + type SerializeTuple = SerializeIdiomOperatorValueTuple; + type SerializeTupleStruct = Impossible; + type SerializeTupleVariant = Impossible; + type SerializeMap = Impossible; + type SerializeStruct = Impossible; + type SerializeStructVariant = Impossible; + + const EXPECTED: &'static str = "an `(Idiom, Operator, Value)`"; + + fn serialize_tuple(self, _len: usize) -> Result { + Ok(SerializeIdiomOperatorValueTuple::default()) + } +} + +#[derive(Default)] +struct SerializeIdiomOperatorValueTuple { + index: usize, + idiom: Option, + operator: Option, + value: Option, +} + +impl serde::ser::SerializeTuple for SerializeIdiomOperatorValueTuple { + type Ok = IdiomOperatorValueTuple; + type Error = Error; + + fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize + ?Sized, + { + match self.index { + 0 => { + self.idiom = Some(Idiom(value.serialize(ser::part::vec::Serializer.wrap())?)); + } + 1 => { + self.operator = Some(value.serialize(ser::operator::Serializer.wrap())?); + } + 2 => { + self.value = Some(value.serialize(ser::value::Serializer.wrap())?); + } + index => { + return Err(Error::custom(format!( + "unexpected tuple index `{index}` for `(Idiom, Operator, Value)`" + ))); + } + } + self.index += 1; + Ok(()) + } + + fn end(self) -> Result { + match (self.idiom, self.operator, self.value) { + (Some(idiom), Some(operator), Some(value)) => Ok((idiom, operator, value)), + _ => Err(Error::custom("`(Idiom, Operator, Value)` missing required value(s)")), + } + } +} + +type IdiomValueTuple = (Idiom, Value); + +struct IdiomValueVecVecSerializer; + +impl ser::Serializer for IdiomValueVecVecSerializer { + type Ok = Vec>; + type Error = Error; + + type SerializeSeq = SerializeIdiomValueVecVec; + type SerializeTuple = Impossible>, Error>; + type SerializeTupleStruct = Impossible>, Error>; + type SerializeTupleVariant = Impossible>, Error>; + type SerializeMap = Impossible>, Error>; + type SerializeStruct = Impossible>, Error>; + type SerializeStructVariant = Impossible>, Error>; + + const EXPECTED: &'static str = "a `Vec>`"; + + fn serialize_seq(self, len: Option) -> Result { + Ok(SerializeIdiomValueVecVec(Vec::with_capacity(len.unwrap_or_default()))) + } +} + +struct SerializeIdiomValueVecVec(Vec>); + +impl serde::ser::SerializeSeq for SerializeIdiomValueVecVec { + type Ok = Vec>; + type Error = Error; + + fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize + ?Sized, + { + self.0.push(value.serialize(IdiomValueVecSerializer.wrap())?); + Ok(()) + } + + fn end(self) -> Result { + Ok(self.0) + } +} + +struct IdiomValueVecSerializer; + +impl ser::Serializer for IdiomValueVecSerializer { + type Ok = Vec; + type Error = Error; + + type SerializeSeq = SerializeIdiomValueVec; + type SerializeTuple = Impossible, Error>; + type SerializeTupleStruct = Impossible, Error>; + type SerializeTupleVariant = Impossible, Error>; + type SerializeMap = Impossible, Error>; + type SerializeStruct = Impossible, Error>; + type SerializeStructVariant = Impossible, Error>; + + const EXPECTED: &'static str = "a `Vec<(Idiom, Value)>`"; + + fn serialize_seq(self, len: Option) -> Result { + Ok(SerializeIdiomValueVec(Vec::with_capacity(len.unwrap_or_default()))) + } +} + +struct SerializeIdiomValueVec(Vec); + +impl serde::ser::SerializeSeq for SerializeIdiomValueVec { + type Ok = Vec; + type Error = Error; + + fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize + ?Sized, + { + self.0.push(value.serialize(IdiomValueTupleSerializer.wrap())?); + Ok(()) + } + + fn end(self) -> Result { + Ok(self.0) + } +} + +struct IdiomValueTupleSerializer; + +impl ser::Serializer for IdiomValueTupleSerializer { + type Ok = IdiomValueTuple; + type Error = Error; + + type SerializeSeq = Impossible; + type SerializeTuple = SerializeIdiomValueTuple; + type SerializeTupleStruct = Impossible; + type SerializeTupleVariant = Impossible; + type SerializeMap = Impossible; + type SerializeStruct = Impossible; + type SerializeStructVariant = Impossible; + + const EXPECTED: &'static str = "an `(Idiom, Value)`"; + + fn serialize_tuple(self, _len: usize) -> Result { + Ok(SerializeIdiomValueTuple::default()) + } +} + +#[derive(Default)] +struct SerializeIdiomValueTuple { + index: usize, + idiom: Option, + value: Option, +} + +impl serde::ser::SerializeTuple for SerializeIdiomValueTuple { + type Ok = IdiomValueTuple; + type Error = Error; + + fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize + ?Sized, + { + match self.index { + 0 => { + self.idiom = Some(Idiom(value.serialize(ser::part::vec::Serializer.wrap())?)); + } + 1 => { + self.value = Some(value.serialize(ser::value::Serializer.wrap())?); + } + index => { + return Err(Error::custom(format!( + "unexpected tuple index `{index}` for `(Idiom, Value)`" + ))); + } + } + self.index += 1; + Ok(()) + } + + fn end(self) -> Result { + match (self.idiom, self.value) { + (Some(idiom), Some(value)) => Ok((idiom, value)), + _ => Err(Error::custom("`(Idiom, Value)` missing required value(s)")), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn empty_expression() { + let data = Data::EmptyExpression; + let serialized = data.serialize(Serializer.wrap()).unwrap(); + assert_eq!(data, serialized); + } + + #[test] + fn set_expression() { + let data = + Data::SetExpression(vec![(Default::default(), Default::default(), Default::default())]); + let serialized = data.serialize(Serializer.wrap()).unwrap(); + assert_eq!(data, serialized); + } + + #[test] + fn unset_expression() { + let data = Data::UnsetExpression(vec![Default::default()]); + let serialized = data.serialize(Serializer.wrap()).unwrap(); + assert_eq!(data, serialized); + } + + #[test] + fn patch_expression() { + let data = Data::PatchExpression(Default::default()); + let serialized = data.serialize(Serializer.wrap()).unwrap(); + assert_eq!(data, serialized); + } + + #[test] + fn merge_expression() { + let data = Data::MergeExpression(Default::default()); + let serialized = data.serialize(Serializer.wrap()).unwrap(); + assert_eq!(data, serialized); + } + + #[test] + fn replace_expression() { + let data = Data::ReplaceExpression(Default::default()); + let serialized = data.serialize(Serializer.wrap()).unwrap(); + assert_eq!(data, serialized); + } + + #[test] + fn content_expression() { + let data = Data::ContentExpression(Default::default()); + let serialized = data.serialize(Serializer.wrap()).unwrap(); + assert_eq!(data, serialized); + } + + #[test] + fn single_expression() { + let data = Data::SingleExpression(Default::default()); + let serialized = data.serialize(Serializer.wrap()).unwrap(); + assert_eq!(data, serialized); + } + + #[test] + fn values_expression() { + let data = Data::ValuesExpression(vec![vec![(Default::default(), Default::default())]]); + let serialized = data.serialize(Serializer.wrap()).unwrap(); + assert_eq!(data, serialized); + } + + #[test] + fn update_expression() { + let data = Data::UpdateExpression(vec![( + Default::default(), + Default::default(), + Default::default(), + )]); + let serialized = data.serialize(Serializer.wrap()).unwrap(); + assert_eq!(data, serialized); + } +} diff --git a/core/src/sql/v2/value/serde/ser/data/opt.rs b/core/src/sql/v2/value/serde/ser/data/opt.rs new file mode 100644 index 00000000..5db2ed3e --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/data/opt.rs @@ -0,0 +1,55 @@ +use crate::err::Error; +use crate::sql::value::serde::ser; +use crate::sql::Data; +use serde::ser::Impossible; +use serde::ser::Serialize; + +pub struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = Option; + type Error = Error; + + type SerializeSeq = Impossible, Error>; + type SerializeTuple = Impossible, Error>; + type SerializeTupleStruct = Impossible, Error>; + type SerializeTupleVariant = Impossible, Error>; + type SerializeMap = Impossible, Error>; + type SerializeStruct = Impossible, Error>; + type SerializeStructVariant = Impossible, Error>; + + const EXPECTED: &'static str = "an `Option`"; + + #[inline] + fn serialize_none(self) -> Result { + Ok(None) + } + + #[inline] + fn serialize_some(self, value: &T) -> Result + where + T: ?Sized + Serialize, + { + Ok(Some(value.serialize(super::Serializer.wrap())?)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ser::Serializer as _; + + #[test] + fn none() { + let option: Option = None; + let serialized = option.serialize(Serializer.wrap()).unwrap(); + assert_eq!(option, serialized); + } + + #[test] + fn some() { + let option = Some(Data::default()); + let serialized = option.serialize(Serializer.wrap()).unwrap(); + assert_eq!(option, serialized); + } +} diff --git a/core/src/sql/v2/value/serde/ser/datetime/mod.rs b/core/src/sql/v2/value/serde/ser/datetime/mod.rs new file mode 100644 index 00000000..aac53d18 --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/datetime/mod.rs @@ -0,0 +1,59 @@ +use crate::err::Error; +use crate::sql::value::serde::ser; +use chrono::offset::Utc; +use chrono::DateTime; +use serde::ser::Error as _; +use serde::ser::Impossible; +use serde::Serialize; +use std::fmt::Display; + +pub(super) struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = DateTime; + type Error = Error; + + type SerializeSeq = Impossible, Error>; + type SerializeTuple = Impossible, Error>; + type SerializeTupleStruct = Impossible, Error>; + type SerializeTupleVariant = Impossible, Error>; + type SerializeMap = Impossible, Error>; + type SerializeStruct = Impossible, Error>; + type SerializeStructVariant = Impossible, Error>; + + const EXPECTED: &'static str = "a struct `DateTime`"; + + #[inline] + fn collect_str(self, value: &T) -> Result + where + T: Display, + { + value.to_string().parse().map_err(Error::custom) + } + + #[inline] + fn serialize_newtype_struct( + self, + _name: &'static str, + value: &T, + ) -> Result + where + T: ?Sized + Serialize, + { + value.serialize(self.wrap()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ser::Serializer as _; + use serde::Serialize; + + #[test] + fn now() { + let dt = Utc::now(); + let serialized = dt.serialize(Serializer.wrap()).unwrap(); + assert_eq!(dt, serialized); + } +} diff --git a/core/src/sql/v2/value/serde/ser/decimal/mod.rs b/core/src/sql/v2/value/serde/ser/decimal/mod.rs new file mode 100644 index 00000000..b240b510 --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/decimal/mod.rs @@ -0,0 +1,41 @@ +use crate::err::Error; +use crate::sql::value::serde::ser; +use rust_decimal::Decimal; +use serde::ser::Error as _; +use serde::ser::Impossible; + +pub(super) struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = Decimal; + type Error = Error; + + type SerializeSeq = Impossible; + type SerializeTuple = Impossible; + type SerializeTupleStruct = Impossible; + type SerializeTupleVariant = Impossible; + type SerializeMap = Impossible; + type SerializeStruct = Impossible; + type SerializeStructVariant = Impossible; + + const EXPECTED: &'static str = "a struct `Decimal`"; + + #[inline] + fn serialize_str(self, value: &str) -> Result { + value.parse::().map_err(Error::custom) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ser::Serializer as _; + use serde::Serialize; + + #[test] + fn from_i32() { + let decimal = Decimal::from(25); + let serialized = Serialize::serialize(&decimal, Serializer.wrap()).unwrap(); + assert_eq!(decimal, serialized); + } +} diff --git a/core/src/sql/v2/value/serde/ser/dir/mod.rs b/core/src/sql/v2/value/serde/ser/dir/mod.rs new file mode 100644 index 00000000..0b892f35 --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/dir/mod.rs @@ -0,0 +1,65 @@ +use crate::err::Error; +use crate::sql::value::serde::ser; +use crate::sql::Dir; +use serde::ser::Error as _; +use serde::ser::Impossible; + +pub(super) struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = Dir; + type Error = Error; + + type SerializeSeq = Impossible; + type SerializeTuple = Impossible; + type SerializeTupleStruct = Impossible; + type SerializeTupleVariant = Impossible; + type SerializeMap = Impossible; + type SerializeStruct = Impossible; + type SerializeStructVariant = Impossible; + + const EXPECTED: &'static str = "an enum `Dir`"; + + #[inline] + fn serialize_unit_variant( + self, + name: &'static str, + _variant_index: u32, + variant: &'static str, + ) -> Result { + match variant { + "In" => Ok(Dir::In), + "Out" => Ok(Dir::Out), + "Both" => Ok(Dir::Both), + variant => Err(Error::custom(format!("unexpected unit variant `{name}::{variant}`"))), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ser::Serializer as _; + use serde::Serialize; + + #[test] + fn r#in() { + let dir = Dir::In; + let serialized = dir.serialize(Serializer.wrap()).unwrap(); + assert_eq!(dir, serialized); + } + + #[test] + fn out() { + let dir = Dir::Out; + let serialized = dir.serialize(Serializer.wrap()).unwrap(); + assert_eq!(dir, serialized); + } + + #[test] + fn both() { + let dir = Dir::Both; + let serialized = dir.serialize(Serializer.wrap()).unwrap(); + assert_eq!(dir, serialized); + } +} diff --git a/core/src/sql/v2/value/serde/ser/distance/mod.rs b/core/src/sql/v2/value/serde/ser/distance/mod.rs new file mode 100644 index 00000000..ef932216 --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/distance/mod.rs @@ -0,0 +1,94 @@ +use crate::err::Error; +use crate::sql::index::Distance; +use crate::sql::value::serde::ser; +use serde::ser::Error as _; +use serde::ser::Impossible; +use serde::Serialize; + +pub(super) struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = Distance; + type Error = Error; + + type SerializeSeq = Impossible; + type SerializeTuple = Impossible; + type SerializeTupleStruct = Impossible; + type SerializeTupleVariant = Impossible; + type SerializeMap = Impossible; + type SerializeStruct = Impossible; + type SerializeStructVariant = Impossible; + + const EXPECTED: &'static str = "an enum `Distance`"; + + #[inline] + fn serialize_unit_variant( + self, + name: &'static str, + _variant_index: u32, + variant: &'static str, + ) -> Result { + match variant { + "Euclidean" => Ok(Distance::Euclidean), + "Manhattan" => Ok(Distance::Manhattan), + "Hamming" => Ok(Distance::Hamming), + variant => Err(Error::custom(format!("unexpected unit variant `{name}::{variant}`"))), + } + } + + #[inline] + fn serialize_newtype_variant( + self, + name: &'static str, + _variant_index: u32, + variant: &'static str, + value: &T, + ) -> Result + where + T: ?Sized + Serialize, + { + match variant { + "Minkowski" => { + Ok(Distance::Minkowski(value.serialize(ser::number::Serializer.wrap())?)) + } + variant => { + Err(Error::custom(format!("unexpected newtype variant `{name}::{variant}`"))) + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::sql::value::serde::ser::Serializer; + use serde::Serialize; + + #[test] + fn distance_euclidean() { + let dist = Distance::Euclidean; + let serialized = dist.serialize(Serializer.wrap()).unwrap(); + assert_eq!(dist, serialized); + } + + #[test] + fn distance_manhattan() { + let dist = Distance::Manhattan; + let serialized = dist.serialize(Serializer.wrap()).unwrap(); + assert_eq!(dist, serialized); + } + + #[test] + fn distance_hamming() { + let dist = Distance::Hamming; + let serialized = dist.serialize(Serializer.wrap()).unwrap(); + assert_eq!(dist, serialized); + } + + #[test] + fn distance_minkowski() { + let dist = Distance::Minkowski(7.into()); + let serialized = dist.serialize(Serializer.wrap()).unwrap(); + assert_eq!(dist, serialized); + } +} diff --git a/core/src/sql/v2/value/serde/ser/duration/mod.rs b/core/src/sql/v2/value/serde/ser/duration/mod.rs new file mode 100644 index 00000000..868d5f96 --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/duration/mod.rs @@ -0,0 +1,96 @@ +pub(super) mod opt; + +use crate::err::Error; +use crate::sql::value::serde::ser; +use ser::Serializer as _; +use serde::ser::Error as _; +use serde::ser::Impossible; +use serde::ser::Serialize; +use std::time::Duration; + +pub(super) struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = Duration; + type Error = Error; + + type SerializeSeq = Impossible; + type SerializeTuple = Impossible; + type SerializeTupleStruct = Impossible; + type SerializeTupleVariant = Impossible; + type SerializeMap = Impossible; + type SerializeStruct = SerializeDuration; + type SerializeStructVariant = Impossible; + + const EXPECTED: &'static str = "a struct `Duration`"; + + #[inline] + fn serialize_struct( + self, + _name: &'static str, + _len: usize, + ) -> Result { + Ok(SerializeDuration::default()) + } + + #[inline] + fn serialize_newtype_struct( + self, + _name: &'static str, + value: &T, + ) -> Result + where + T: ?Sized + Serialize, + { + value.serialize(self.wrap()) + } +} + +#[derive(Default)] +pub(super) struct SerializeDuration { + secs: Option, + nanos: Option, +} + +impl serde::ser::SerializeStruct for SerializeDuration { + type Ok = Duration; + type Error = Error; + + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Error> + where + T: ?Sized + Serialize, + { + match key { + "secs" => { + self.secs = Some(value.serialize(ser::primitive::u64::Serializer.wrap())?); + } + "nanos" => { + self.nanos = Some(value.serialize(ser::primitive::u32::Serializer.wrap())?); + } + key => { + return Err(Error::custom(format!("unexpected field `Duration::{key}`"))); + } + } + Ok(()) + } + + fn end(self) -> Result { + match (self.secs, self.nanos) { + (Some(secs), Some(nanos)) => Ok(Duration::new(secs, nanos)), + _ => Err(Error::custom("`Duration` missing required field(s)")), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde::Serialize; + + #[test] + fn default() { + let duration = Duration::default(); + let serialized = duration.serialize(Serializer.wrap()).unwrap(); + assert_eq!(duration, serialized); + } +} diff --git a/core/src/sql/v2/value/serde/ser/duration/opt.rs b/core/src/sql/v2/value/serde/ser/duration/opt.rs new file mode 100644 index 00000000..1d063e0d --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/duration/opt.rs @@ -0,0 +1,55 @@ +use crate::err::Error; +use crate::sql::value::serde::ser; +use serde::ser::Impossible; +use serde::ser::Serialize; +use std::time::Duration; + +pub struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = Option; + type Error = Error; + + type SerializeSeq = Impossible, Error>; + type SerializeTuple = Impossible, Error>; + type SerializeTupleStruct = Impossible, Error>; + type SerializeTupleVariant = Impossible, Error>; + type SerializeMap = Impossible, Error>; + type SerializeStruct = Impossible, Error>; + type SerializeStructVariant = Impossible, Error>; + + const EXPECTED: &'static str = "an `Option`"; + + #[inline] + fn serialize_none(self) -> Result { + Ok(None) + } + + #[inline] + fn serialize_some(self, value: &T) -> Result + where + T: ?Sized + Serialize, + { + Ok(Some(value.serialize(super::Serializer.wrap())?)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ser::Serializer as _; + + #[test] + fn none() { + let option: Option = None; + let serialized = option.serialize(Serializer.wrap()).unwrap(); + assert_eq!(option, serialized); + } + + #[test] + fn some() { + let option = Some(Duration::default()); + let serialized = option.serialize(Serializer.wrap()).unwrap(); + assert_eq!(option, serialized); + } +} diff --git a/core/src/sql/v2/value/serde/ser/edges/mod.rs b/core/src/sql/v2/value/serde/ser/edges/mod.rs new file mode 100644 index 00000000..d234d8c4 --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/edges/mod.rs @@ -0,0 +1,98 @@ +use crate::err::Error; +use crate::sql::value::serde::ser; +use crate::sql::Dir; +use crate::sql::Edges; +use crate::sql::Tables; +use crate::sql::Thing; +use ser::Serializer as _; +use serde::ser::Error as _; +use serde::ser::Impossible; +use serde::ser::Serialize; + +pub(super) struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = Edges; + type Error = Error; + + type SerializeSeq = Impossible; + type SerializeTuple = Impossible; + type SerializeTupleStruct = Impossible; + type SerializeTupleVariant = Impossible; + type SerializeMap = Impossible; + type SerializeStruct = SerializeEdges; + type SerializeStructVariant = Impossible; + + const EXPECTED: &'static str = "a struct `Edges`"; + + #[inline] + fn serialize_struct( + self, + _name: &'static str, + _len: usize, + ) -> Result { + Ok(SerializeEdges::default()) + } +} + +#[derive(Default)] +pub(super) struct SerializeEdges { + dir: Option, + from: Option, + what: Option, +} + +impl serde::ser::SerializeStruct for SerializeEdges { + type Ok = Edges; + type Error = Error; + + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Error> + where + T: ?Sized + Serialize, + { + match key { + "dir" => { + self.dir = Some(value.serialize(ser::dir::Serializer.wrap())?); + } + "from" => { + self.from = Some(value.serialize(ser::thing::Serializer.wrap())?); + } + "what" => { + self.what = Some(Tables(value.serialize(ser::table::vec::Serializer.wrap())?)); + } + key => { + return Err(Error::custom(format!("unexpected field `Edges::{key}`"))); + } + } + Ok(()) + } + + fn end(self) -> Result { + match (self.dir, self.from, self.what) { + (Some(dir), Some(from), Some(what)) => Ok(Edges { + dir, + from, + what, + }), + _ => Err(Error::custom("`Edges` missing required field(s)")), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::sql::thing; + use serde::Serialize; + + #[test] + fn edges() { + let edges = Edges { + dir: Dir::Both, + from: thing("foo:bar").unwrap(), + what: Tables(Vec::new()), + }; + let serialized = edges.serialize(Serializer.wrap()).unwrap(); + assert_eq!(edges, serialized); + } +} diff --git a/core/src/sql/v2/value/serde/ser/explain/mod.rs b/core/src/sql/v2/value/serde/ser/explain/mod.rs new file mode 100644 index 00000000..0db91d8e --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/explain/mod.rs @@ -0,0 +1 @@ +pub(super) mod opt; diff --git a/core/src/sql/v2/value/serde/ser/explain/opt.rs b/core/src/sql/v2/value/serde/ser/explain/opt.rs new file mode 100644 index 00000000..0218b9e3 --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/explain/opt.rs @@ -0,0 +1,74 @@ +use crate::err::Error; +use crate::sql::value::serde::ser; +use crate::sql::Explain; +use serde::ser::Impossible; +use serde::ser::Serialize; + +pub struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = Option; + type Error = Error; + + type SerializeSeq = Impossible, Error>; + type SerializeTuple = Impossible, Error>; + type SerializeTupleStruct = Impossible, Error>; + type SerializeTupleVariant = Impossible, Error>; + type SerializeMap = Impossible, Error>; + type SerializeStruct = Impossible, Error>; + type SerializeStructVariant = Impossible, Error>; + + const EXPECTED: &'static str = "an `Option`"; + + #[inline] + fn serialize_none(self) -> Result { + Ok(None) + } + + #[inline] + fn serialize_some(self, value: &T) -> Result + where + T: ?Sized + Serialize, + { + value.serialize(self.wrap()) + } + + #[inline] + fn serialize_newtype_struct( + self, + _name: &'static str, + value: &T, + ) -> Result + where + T: ?Sized + Serialize, + { + Ok(Some(Explain(value.serialize(ser::primitive::bool::Serializer.wrap())?))) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ser::Serializer as _; + + #[test] + fn none() { + let option: Option = None; + let serialized = option.serialize(Serializer.wrap()).unwrap(); + assert_eq!(option, serialized); + } + + #[test] + fn some_default() { + let option = Some(Explain::default()); + let serialized = option.serialize(Serializer.wrap()).unwrap(); + assert_eq!(option, serialized); + } + + #[test] + fn some_full() { + let option = Some(Explain(true)); + let serialized = option.serialize(Serializer.wrap()).unwrap(); + assert_eq!(option, serialized); + } +} diff --git a/core/src/sql/v2/value/serde/ser/expression/mod.rs b/core/src/sql/v2/value/serde/ser/expression/mod.rs new file mode 100644 index 00000000..391913e6 --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/expression/mod.rs @@ -0,0 +1,190 @@ +use crate::err::Error; +use crate::sql::value::serde::ser; +use crate::sql::Expression; +use crate::sql::Operator; +use crate::sql::Value; +use ser::Serializer as _; +use serde::ser::Error as _; +use serde::ser::Impossible; +use serde::ser::Serialize; + +pub(super) struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = Expression; + type Error = Error; + + type SerializeSeq = Impossible; + type SerializeTuple = Impossible; + type SerializeTupleStruct = Impossible; + type SerializeTupleVariant = Impossible; + type SerializeMap = Impossible; + type SerializeStruct = Impossible; + type SerializeStructVariant = SerializeExpression; + + const EXPECTED: &'static str = "an enum `Expression`"; + + #[inline] + fn serialize_struct_variant( + self, + name: &'static str, + _variant_index: u32, + variant: &'static str, + _len: usize, + ) -> Result { + debug_assert_eq!(name, crate::sql::expression::TOKEN); + match variant { + "Unary" => Ok(SerializeExpression::Unary(Default::default())), + "Binary" => Ok(SerializeExpression::Binary(Default::default())), + _ => Err(Error::custom(format!("unexpected `Expression::{name}`"))), + } + } +} + +pub(super) enum SerializeExpression { + Unary(SerializeUnary), + Binary(SerializeBinary), +} + +impl serde::ser::SerializeStructVariant for SerializeExpression { + type Ok = Expression; + type Error = Error; + + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Error> + where + T: ?Sized + Serialize, + { + match self { + Self::Unary(unary) => unary.serialize_field(key, value), + Self::Binary(binary) => binary.serialize_field(key, value), + } + } + + fn end(self) -> Result { + match self { + Self::Unary(unary) => unary.end(), + Self::Binary(binary) => binary.end(), + } + } +} + +#[derive(Default)] +pub(super) struct SerializeUnary { + o: Option, + v: Option, +} + +impl serde::ser::SerializeStructVariant for SerializeUnary { + type Ok = Expression; + type Error = Error; + + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Error> + where + T: ?Sized + Serialize, + { + match key { + "o" => { + self.o = Some(value.serialize(ser::operator::Serializer.wrap())?); + } + "v" => { + self.v = Some(value.serialize(ser::value::Serializer.wrap())?); + } + key => { + return Err(Error::custom(format!( + "unexpected field `Expression::Unary{{{key}}}`" + ))); + } + } + Ok(()) + } + + fn end(self) -> Result { + match (self.o, self.v) { + (Some(o), Some(v)) => Ok(Expression::Unary { + o, + v, + }), + _ => Err(Error::custom("`Expression::Unary` missing required field(s)")), + } + } +} + +#[derive(Default)] +pub(super) struct SerializeBinary { + l: Option, + o: Option, + r: Option, +} + +impl serde::ser::SerializeStructVariant for SerializeBinary { + type Ok = Expression; + type Error = Error; + + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Error> + where + T: ?Sized + Serialize, + { + match key { + "l" => { + self.l = Some(value.serialize(ser::value::Serializer.wrap())?); + } + "o" => { + self.o = Some(value.serialize(ser::operator::Serializer.wrap())?); + } + "r" => { + self.r = Some(value.serialize(ser::value::Serializer.wrap())?); + } + key => { + return Err(Error::custom(format!( + "unexpected field `Expression::Binary{{{key}}}`" + ))); + } + } + Ok(()) + } + + fn end(self) -> Result { + match (self.l, self.o, self.r) { + (Some(l), Some(o), Some(r)) => Ok(Expression::Binary { + l, + o, + r, + }), + _ => Err(Error::custom("`Expression::Binary` missing required field(s)")), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde::Serialize; + + #[test] + fn default() { + let expression = Expression::default(); + let serialized = expression.serialize(Serializer.wrap()).unwrap(); + assert_eq!(expression, serialized); + } + + #[test] + fn unary() { + let expression = Expression::Unary { + o: Operator::Not, + v: "Bar".into(), + }; + let serialized = expression.serialize(Serializer.wrap()).unwrap(); + assert_eq!(expression, serialized); + } + + #[test] + fn foo_equals_bar() { + let expression = Expression::Binary { + l: "foo".into(), + o: Operator::Equal, + r: "Bar".into(), + }; + let serialized = expression.serialize(Serializer.wrap()).unwrap(); + assert_eq!(expression, serialized); + } +} diff --git a/core/src/sql/v2/value/serde/ser/fetch/mod.rs b/core/src/sql/v2/value/serde/ser/fetch/mod.rs new file mode 100644 index 00000000..5537a772 --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/fetch/mod.rs @@ -0,0 +1 @@ +pub(super) mod vec; diff --git a/core/src/sql/v2/value/serde/ser/fetch/vec/mod.rs b/core/src/sql/v2/value/serde/ser/fetch/vec/mod.rs new file mode 100644 index 00000000..bd1862a0 --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/fetch/vec/mod.rs @@ -0,0 +1,80 @@ +pub mod opt; + +use crate::err::Error; +use crate::sql::value::serde::ser; +use crate::sql::Fetch; +use crate::sql::Idiom; +use ser::Serializer as _; +use serde::ser::Impossible; +use serde::ser::Serialize; + +pub struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = Vec; + type Error = Error; + + type SerializeSeq = SerializeFetchVec; + type SerializeTuple = Impossible, Error>; + type SerializeTupleStruct = Impossible, Error>; + type SerializeTupleVariant = Impossible, Error>; + type SerializeMap = Impossible, Error>; + type SerializeStruct = Impossible, Error>; + type SerializeStructVariant = Impossible, Error>; + + const EXPECTED: &'static str = "a `Vec`"; + + fn serialize_seq(self, len: Option) -> Result { + Ok(SerializeFetchVec(Vec::with_capacity(len.unwrap_or_default()))) + } + + #[inline] + fn serialize_newtype_struct( + self, + _name: &'static str, + value: &T, + ) -> Result + where + T: ?Sized + Serialize, + { + value.serialize(self.wrap()) + } +} + +pub struct SerializeFetchVec(Vec); + +impl serde::ser::SerializeSeq for SerializeFetchVec { + type Ok = Vec; + type Error = Error; + + fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize + ?Sized, + { + self.0.push(Fetch(Idiom(value.serialize(ser::part::vec::Serializer.wrap())?))); + Ok(()) + } + + fn end(self) -> Result { + Ok(self.0) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn empty() { + let vec: Vec = Vec::new(); + let serialized = vec.serialize(Serializer.wrap()).unwrap(); + assert_eq!(vec, serialized); + } + + #[test] + fn vec() { + let vec = vec![Fetch::default()]; + let serialized = vec.serialize(Serializer.wrap()).unwrap(); + assert_eq!(vec, serialized); + } +} diff --git a/core/src/sql/v2/value/serde/ser/fetch/vec/opt.rs b/core/src/sql/v2/value/serde/ser/fetch/vec/opt.rs new file mode 100644 index 00000000..c8431a3f --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/fetch/vec/opt.rs @@ -0,0 +1,55 @@ +use crate::err::Error; +use crate::sql::value::serde::ser; +use crate::sql::Fetch; +use serde::ser::Impossible; +use serde::ser::Serialize; + +pub struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = Option>; + type Error = Error; + + type SerializeSeq = Impossible>, Error>; + type SerializeTuple = Impossible>, Error>; + type SerializeTupleStruct = Impossible>, Error>; + type SerializeTupleVariant = Impossible>, Error>; + type SerializeMap = Impossible>, Error>; + type SerializeStruct = Impossible>, Error>; + type SerializeStructVariant = Impossible>, Error>; + + const EXPECTED: &'static str = "an `Option>`"; + + #[inline] + fn serialize_none(self) -> Result { + Ok(None) + } + + #[inline] + fn serialize_some(self, value: &T) -> Result + where + T: ?Sized + Serialize, + { + Ok(Some(value.serialize(super::Serializer.wrap())?)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ser::Serializer as _; + + #[test] + fn none() { + let option: Option> = None; + let serialized = option.serialize(Serializer.wrap()).unwrap(); + assert_eq!(option, serialized); + } + + #[test] + fn some() { + let option = Some(vec![Fetch::default()]); + let serialized = option.serialize(Serializer.wrap()).unwrap(); + assert_eq!(option, serialized); + } +} diff --git a/core/src/sql/v2/value/serde/ser/fetchs/mod.rs b/core/src/sql/v2/value/serde/ser/fetchs/mod.rs new file mode 100644 index 00000000..0db91d8e --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/fetchs/mod.rs @@ -0,0 +1 @@ +pub(super) mod opt; diff --git a/core/src/sql/v2/value/serde/ser/fetchs/opt.rs b/core/src/sql/v2/value/serde/ser/fetchs/opt.rs new file mode 100644 index 00000000..2292e569 --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/fetchs/opt.rs @@ -0,0 +1,55 @@ +use crate::err::Error; +use crate::sql::value::serde::ser; +use crate::sql::Fetchs; +use serde::ser::Impossible; +use serde::ser::Serialize; + +pub struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = Option; + type Error = Error; + + type SerializeSeq = Impossible, Error>; + type SerializeTuple = Impossible, Error>; + type SerializeTupleStruct = Impossible, Error>; + type SerializeTupleVariant = Impossible, Error>; + type SerializeMap = Impossible, Error>; + type SerializeStruct = Impossible, Error>; + type SerializeStructVariant = Impossible, Error>; + + const EXPECTED: &'static str = "an `Option`"; + + #[inline] + fn serialize_none(self) -> Result { + Ok(None) + } + + #[inline] + fn serialize_some(self, value: &T) -> Result + where + T: ?Sized + Serialize, + { + Ok(Some(Fetchs(value.serialize(ser::fetch::vec::Serializer.wrap())?))) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ser::Serializer as _; + + #[test] + fn none() { + let option: Option = None; + let serialized = option.serialize(Serializer.wrap()).unwrap(); + assert_eq!(option, serialized); + } + + #[test] + fn some() { + let option = Some(Fetchs::default()); + let serialized = option.serialize(Serializer.wrap()).unwrap(); + assert_eq!(option, serialized); + } +} diff --git a/core/src/sql/v2/value/serde/ser/field/mod.rs b/core/src/sql/v2/value/serde/ser/field/mod.rs new file mode 100644 index 00000000..2b8972b2 --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/field/mod.rs @@ -0,0 +1,156 @@ +pub(super) mod vec; + +use crate::err::Error; +use crate::sql::value::serde::ser; +use crate::sql::Field; +use crate::sql::Idiom; +use crate::sql::Value; +use ser::Serializer as _; +use serde::ser::Error as _; +use serde::ser::Impossible; +use serde::ser::Serialize; + +pub(super) struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = Field; + type Error = Error; + + type SerializeSeq = Impossible; + type SerializeTuple = Impossible; + type SerializeTupleStruct = Impossible; + type SerializeTupleVariant = Impossible; + type SerializeMap = Impossible; + type SerializeStruct = Impossible; + type SerializeStructVariant = SerializeValueIdiomTuple; + + const EXPECTED: &'static str = "an enum `Field`"; + + #[inline] + fn serialize_unit_variant( + self, + name: &'static str, + _variant_index: u32, + variant: &'static str, + ) -> Result { + match variant { + "All" => Ok(Field::All), + variant => Err(Error::custom(format!("unexpected unit variant `{name}::{variant}`"))), + } + } + + fn serialize_struct_variant( + self, + name: &'static str, + _variant_index: u32, + variant: &'static str, + _len: usize, + ) -> Result { + match variant { + "Single" => Ok(SerializeValueIdiomTuple::default()), + variant => Err(Error::custom(format!("unexpected struct variant `{name}::{variant}`"))), + } + } +} + +#[derive(Default)] +pub(super) struct SerializeValueIdiomTuple { + value: Option, + idiom: Option>, +} + +impl serde::ser::SerializeStructVariant for SerializeValueIdiomTuple { + type Ok = Field; + type Error = Error; + + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> + where + T: Serialize + ?Sized, + { + match key { + "expr" => { + self.value = Some(value.serialize(ser::value::Serializer.wrap())?); + } + "alias" => { + self.idiom = Some(value.serialize(SerializeOptionIdiom.wrap())?); + } + key => { + return Err(Error::custom(format!("unexpected `Field::Single` field `{key}`"))); + } + } + Ok(()) + } + + fn end(self) -> Result { + match (self.value, self.idiom) { + (Some(expr), Some(alias)) => Ok(Field::Single { + expr, + alias, + }), + _ => Err(Error::custom("`Field::Single` missing required value(s)")), + } + } +} + +#[derive(Default)] +struct SerializeOptionIdiom; + +impl ser::Serializer for SerializeOptionIdiom { + type Ok = Option; + type Error = Error; + + type SerializeSeq = Impossible; + type SerializeTuple = Impossible; + type SerializeTupleStruct = Impossible; + type SerializeTupleVariant = Impossible; + type SerializeMap = Impossible; + type SerializeStruct = Impossible; + type SerializeStructVariant = Impossible; + + const EXPECTED: &'static str = "an enum `Option`"; + + fn serialize_none(self) -> Result { + Ok(None) + } + + fn serialize_some(self, value: &T) -> Result + where + T: ?Sized + Serialize, + { + let idiom = Idiom(value.serialize(ser::part::vec::Serializer.wrap())?); + Ok(Some(idiom)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde::Serialize; + + #[test] + fn all() { + let field = Field::All; + let serialized = field.serialize(Serializer.wrap()).unwrap(); + assert_eq!(field, serialized); + } + + #[test] + fn alone() { + let field = Field::Single { + expr: Default::default(), + alias: None, + }; + let serialized = field.serialize(Serializer.wrap()).unwrap(); + assert_eq!(field, serialized); + } + + #[test] + fn alias() { + let field = Field::Single { + expr: Default::default(), + alias: Some(Default::default()), + }; + let serialized = field.serialize(Serializer.wrap()).unwrap(); + assert_eq!(field, serialized); + } +} diff --git a/core/src/sql/v2/value/serde/ser/field/vec.rs b/core/src/sql/v2/value/serde/ser/field/vec.rs new file mode 100644 index 00000000..65216f7a --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/field/vec.rs @@ -0,0 +1,77 @@ +use crate::err::Error; +use crate::sql::value::serde::ser; +use crate::sql::Field; +use ser::Serializer as _; +use serde::ser::Impossible; +use serde::ser::Serialize; + +pub struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = Vec; + type Error = Error; + + type SerializeSeq = SerializeFieldVec; + type SerializeTuple = Impossible, Error>; + type SerializeTupleStruct = Impossible, Error>; + type SerializeTupleVariant = Impossible, Error>; + type SerializeMap = Impossible, Error>; + type SerializeStruct = Impossible, Error>; + type SerializeStructVariant = Impossible, Error>; + + const EXPECTED: &'static str = "a `Vec`"; + + fn serialize_seq(self, len: Option) -> Result { + Ok(SerializeFieldVec(Vec::with_capacity(len.unwrap_or_default()))) + } + + #[inline] + fn serialize_newtype_struct( + self, + _name: &'static str, + value: &T, + ) -> Result + where + T: ?Sized + Serialize, + { + value.serialize(self.wrap()) + } +} + +pub struct SerializeFieldVec(Vec); + +impl serde::ser::SerializeSeq for SerializeFieldVec { + type Ok = Vec; + type Error = Error; + + fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize + ?Sized, + { + self.0.push(value.serialize(ser::field::Serializer.wrap())?); + Ok(()) + } + + fn end(self) -> Result { + Ok(self.0) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn empty() { + let vec: Vec = Vec::new(); + let serialized = vec.serialize(Serializer.wrap()).unwrap(); + assert_eq!(vec, serialized); + } + + #[test] + fn vec() { + let vec = vec![Field::default()]; + let serialized = vec.serialize(Serializer.wrap()).unwrap(); + assert_eq!(vec, serialized); + } +} diff --git a/core/src/sql/v2/value/serde/ser/fields/mod.rs b/core/src/sql/v2/value/serde/ser/fields/mod.rs new file mode 100644 index 00000000..72cdfb26 --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/fields/mod.rs @@ -0,0 +1,90 @@ +use crate::err::Error; +use crate::sql::value::serde::ser; +use crate::sql::Field; +use crate::sql::Fields; +use ser::Serializer as _; +use serde::ser::Error as _; +use serde::ser::Impossible; +use serde::ser::Serialize; + +pub(super) struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = Fields; + type Error = Error; + + type SerializeSeq = Impossible; + type SerializeTuple = Impossible; + type SerializeTupleStruct = SerializeFields; + type SerializeTupleVariant = Impossible; + type SerializeMap = Impossible; + type SerializeStruct = Impossible; + type SerializeStructVariant = Impossible; + + const EXPECTED: &'static str = "a struct `Fields`"; + + fn serialize_tuple_struct( + self, + _name: &'static str, + _len: usize, + ) -> Result { + Ok(SerializeFields::default()) + } +} + +#[derive(Default)] +pub(super) struct SerializeFields { + index: usize, + fields: Option>, + boolean: Option, +} + +impl serde::ser::SerializeTupleStruct for SerializeFields { + type Ok = Fields; + type Error = Error; + + fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize + ?Sized, + { + match self.index { + 0 => { + self.fields = Some(value.serialize(ser::field::vec::Serializer.wrap())?); + } + 1 => { + self.boolean = Some(value.serialize(ser::primitive::bool::Serializer.wrap())?); + } + index => { + return Err(Error::custom(format!("unexpected `Fields` index `{index}`"))); + } + } + self.index += 1; + Ok(()) + } + + fn end(self) -> Result { + match (self.fields, self.boolean) { + (Some(fields), Some(boolean)) => Ok(Fields(fields, boolean)), + _ => Err(Error::custom("`Fields` missing required value(s)")), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn default() { + let fields = Fields::default(); + let serialized = fields.serialize(Serializer.wrap()).unwrap(); + assert_eq!(fields, serialized); + } + + #[test] + fn all() { + let fields = Fields(vec![Field::All], true); + let serialized = fields.serialize(Serializer.wrap()).unwrap(); + assert_eq!(fields, serialized); + } +} diff --git a/core/src/sql/v2/value/serde/ser/filter/mod.rs b/core/src/sql/v2/value/serde/ser/filter/mod.rs new file mode 100644 index 00000000..07bc3edd --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/filter/mod.rs @@ -0,0 +1,154 @@ +pub(super) mod vec; + +use crate::err::Error; +use crate::sql::filter::Filter; +use crate::sql::value::serde::ser; +use ser::Serializer as _; +use serde::ser::Error as _; +use serde::ser::Impossible; +use serde::ser::Serialize; + +pub(super) struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = Filter; + type Error = Error; + + type SerializeSeq = Impossible; + type SerializeTuple = Impossible; + type SerializeTupleStruct = Impossible; + type SerializeTupleVariant = SerializeFilter; + type SerializeMap = Impossible; + type SerializeStruct = Impossible; + type SerializeStructVariant = Impossible; + + const EXPECTED: &'static str = "an enum `Filter`"; + + #[inline] + fn serialize_unit_variant( + self, + name: &'static str, + _variant_index: u32, + variant: &'static str, + ) -> Result { + match variant { + "Ascii" => Ok(Filter::Ascii), + "Lowercase" => Ok(Filter::Lowercase), + "Uppercase" => Ok(Filter::Uppercase), + variant => Err(Error::custom(format!("unexpected unit variant `{name}::{variant}`"))), + } + } + + #[inline] + fn serialize_newtype_variant( + self, + name: &'static str, + _variant_index: u32, + variant: &'static str, + value: &T, + ) -> Result + where + T: ?Sized + Serialize, + { + match variant { + "Snowball" => Ok(Filter::Snowball(value.serialize(ser::language::Serializer.wrap())?)), + variant => { + Err(Error::custom(format!("unexpected newtype variant `{name}::{variant}`"))) + } + } + } + + fn serialize_tuple_variant( + self, + name: &'static str, + _variant_index: u32, + variant: &'static str, + _len: usize, + ) -> Result { + let inner = match variant { + "EdgeNgram" => Inner::EdgeNgram(Default::default(), Default::default()), + "Ngram" => Inner::Ngram(Default::default(), Default::default()), + variant => { + return Err(Error::custom(format!("unexpected tuple variant `{name}::{variant}`"))); + } + }; + Ok(SerializeFilter { + inner, + index: 0, + }) + } +} + +pub(super) struct SerializeFilter { + index: usize, + inner: Inner, +} + +enum Inner { + EdgeNgram(u16, u16), + Ngram(u16, u16), +} + +impl serde::ser::SerializeTupleVariant for SerializeFilter { + type Ok = Filter; + type Error = Error; + + fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize + ?Sized, + { + match (self.index, &mut self.inner) { + (0, Inner::EdgeNgram(ref mut var, _) | Inner::Ngram(ref mut var, _)) => { + *var = value.serialize(ser::primitive::u16::Serializer.wrap())?; + } + (1, Inner::EdgeNgram(_, ref mut var) | Inner::Ngram(_, ref mut var)) => { + *var = value.serialize(ser::primitive::u16::Serializer.wrap())?; + } + (index, inner) => { + let variant = match inner { + Inner::EdgeNgram(..) => "EdgeNgram", + Inner::Ngram(..) => "Ngram", + }; + return Err(Error::custom(format!( + "unexpected `Filter::{variant}` index `{index}`" + ))); + } + } + self.index += 1; + Ok(()) + } + + fn end(self) -> Result { + match self.inner { + Inner::EdgeNgram(one, two) => Ok(Filter::EdgeNgram(one, two)), + Inner::Ngram(one, two) => Ok(Filter::Ngram(one, two)), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::sql::language::Language; + + #[test] + fn ascii() { + let filter = Filter::Ascii; + let serialized = filter.serialize(Serializer.wrap()).unwrap(); + assert_eq!(filter, serialized); + } + + #[test] + fn lowercase() { + let filter = Filter::Lowercase; + let serialized = filter.serialize(Serializer.wrap()).unwrap(); + assert_eq!(filter, serialized); + } + + #[test] + fn snowball() { + let filter = Filter::Snowball(Language::English); + let serialized = filter.serialize(Serializer.wrap()).unwrap(); + assert_eq!(filter, serialized); + } +} diff --git a/core/src/sql/v2/value/serde/ser/filter/vec/mod.rs b/core/src/sql/v2/value/serde/ser/filter/vec/mod.rs new file mode 100644 index 00000000..4806b734 --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/filter/vec/mod.rs @@ -0,0 +1,79 @@ +pub mod opt; + +use crate::err::Error; +use crate::sql::filter::Filter; +use crate::sql::value::serde::ser; +use ser::Serializer as _; +use serde::ser::Impossible; +use serde::ser::Serialize; + +pub struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = Vec; + type Error = Error; + + type SerializeSeq = SerializeFilterVec; + type SerializeTuple = Impossible, Error>; + type SerializeTupleStruct = Impossible, Error>; + type SerializeTupleVariant = Impossible, Error>; + type SerializeMap = Impossible, Error>; + type SerializeStruct = Impossible, Error>; + type SerializeStructVariant = Impossible, Error>; + + const EXPECTED: &'static str = "a `Vec`"; + + fn serialize_seq(self, len: Option) -> Result { + Ok(SerializeFilterVec(Vec::with_capacity(len.unwrap_or_default()))) + } + + #[inline] + fn serialize_newtype_struct( + self, + _name: &'static str, + value: &T, + ) -> Result + where + T: ?Sized + Serialize, + { + value.serialize(self.wrap()) + } +} + +pub struct SerializeFilterVec(Vec); + +impl serde::ser::SerializeSeq for SerializeFilterVec { + type Ok = Vec; + type Error = Error; + + fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> + where + T: Serialize + ?Sized, + { + self.0.push(value.serialize(super::Serializer.wrap())?); + Ok(()) + } + + fn end(self) -> Result { + Ok(self.0) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn empty() { + let vec: Vec = Vec::new(); + let serialized = vec.serialize(Serializer.wrap()).unwrap(); + assert_eq!(vec, serialized); + } + + #[test] + fn vec() { + let vec = vec![Filter::Ascii]; + let serialized = vec.serialize(Serializer.wrap()).unwrap(); + assert_eq!(vec, serialized); + } +} diff --git a/core/src/sql/v2/value/serde/ser/filter/vec/opt.rs b/core/src/sql/v2/value/serde/ser/filter/vec/opt.rs new file mode 100644 index 00000000..1c015fa6 --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/filter/vec/opt.rs @@ -0,0 +1,55 @@ +use crate::err::Error; +use crate::sql::filter::Filter; +use crate::sql::value::serde::ser; +use serde::ser::Impossible; +use serde::ser::Serialize; + +pub struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = Option>; + type Error = Error; + + type SerializeSeq = Impossible>, Error>; + type SerializeTuple = Impossible>, Error>; + type SerializeTupleStruct = Impossible>, Error>; + type SerializeTupleVariant = Impossible>, Error>; + type SerializeMap = Impossible>, Error>; + type SerializeStruct = Impossible>, Error>; + type SerializeStructVariant = Impossible>, Error>; + + const EXPECTED: &'static str = "an `Option>`"; + + #[inline] + fn serialize_none(self) -> Result { + Ok(None) + } + + #[inline] + fn serialize_some(self, value: &T) -> Result + where + T: ?Sized + Serialize, + { + Ok(Some(value.serialize(super::Serializer.wrap())?)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ser::Serializer as _; + + #[test] + fn none() { + let option: Option> = None; + let serialized = option.serialize(Serializer.wrap()).unwrap(); + assert_eq!(option, serialized); + } + + #[test] + fn some() { + let option = Some(vec![Filter::Ascii]); + let serialized = option.serialize(Serializer.wrap()).unwrap(); + assert_eq!(option, serialized); + } +} diff --git a/core/src/sql/v2/value/serde/ser/function/mod.rs b/core/src/sql/v2/value/serde/ser/function/mod.rs new file mode 100644 index 00000000..55a478ab --- /dev/null +++ b/core/src/sql/v2/value/serde/ser/function/mod.rs @@ -0,0 +1,133 @@ +use crate::err::Error; +use crate::sql::value::serde::ser; +use crate::sql::Function; +use crate::sql::Script; +use crate::sql::Value; +use ser::Serializer as _; +use serde::ser::Error as _; +use serde::ser::Impossible; +use serde::ser::Serialize; + +pub(super) struct Serializer; + +impl ser::Serializer for Serializer { + type Ok = Function; + type Error = Error; + + type SerializeSeq = Impossible; + type SerializeTuple = Impossible; + type SerializeTupleStruct = Impossible; + type SerializeTupleVariant = SerializeFunction; + type SerializeMap = Impossible; + type SerializeStruct = Impossible; + type SerializeStructVariant = Impossible; + + const EXPECTED: &'static str = "an enum `Function`"; + + fn serialize_tuple_variant( + self, + name: &'static str, + _variant_index: u32, + variant: &'static str, + _len: usize, + ) -> Result { + let inner = match variant { + "Normal" => Inner::Normal(None, None), + "Custom" => Inner::Custom(None, None), + "Script" => Inner::Script(None, None), + variant => { + return Err(Error::custom(format!("unexpected tuple variant `{name}::{variant}`"))); + } + }; + Ok(SerializeFunction { + inner, + index: 0, + }) + } +} + +pub(super) struct SerializeFunction { + index: usize, + inner: Inner, +} + +enum Inner { + Normal(Option, Option>), + Custom(Option, Option>), + Script(Option