prisma / prisma Goto Github PK
View Code? Open in Web Editor NEWNext-generation ORM for Node.js & TypeScript | PostgreSQL, MySQL, MariaDB, SQL Server, SQLite, MongoDB and CockroachDB
Home Page: https://www.prisma.io
License: Apache License 2.0
Next-generation ORM for Node.js & TypeScript | PostgreSQL, MySQL, MariaDB, SQL Server, SQLite, MongoDB and CockroachDB
Home Page: https://www.prisma.io
License: Apache License 2.0
The Decimal
type currently maps to f32
, which will certainly lead to overflows.
Copying text feels weird
https://www.loom.com/share/3b4db796029842a1a73cfc27a5c113d2
We should listen on data
instead of keypress
for text inputs
CLI gets stuck on prisma2 lift up
when the MySQL URL contains a schema that does not exist.
Do not show SQLite3 in option till introspection is not available for SQLite3.
Whenever performing operations on objects in your application which get directly persisted to the database can involve:
A solution to this problem is referred to as a โUnit of Workโ which proposes to keep track of changes done within a transaction that can affect the database, and taking care of figuring out the changes which need to be done at the database level as a result.
See RFC
See also internal notes
model Todo {
id: Int @id
text: String
author: Owner @relation(name: "AuthorTodo")
delegatedTo: Owner? @relation(name: "DelegatedToTodo")
}
model Owner {
id: Int @id
name: String?
todos: Todo[] @relation(name: "AuthorTodo")
}
Fails with the following trace:
$ [todo-matrix]$ yarn prisma2 generate
yarn run v1.15.2
$ /Users/divyendusingh/Documents/prisma/support/feature/todo-matrix/node_modules/.bin/prisma2 generate
Generating Photon to /Users/divyendusingh/Documents/prisma/support/feature/todo-matrix/node_modules/@generated/photon
Engine path: /Users/divyendusingh/Documents/prisma/support/feature/todo-matrix/node_modules/@prisma/prototype/runtime/prisma
/Users/divyendusingh/Documents/prisma/support/feature/todo-matrix/node_modules/@prisma/prototype/build/index.js:17018
throw new Engine_1.PhotonError(`Error in query engine: ` + this.errorLogs, undefined, undefined, this.errorLogs);
^
PhotonError: Error in query engine: thread 'main' panicked at 'Did not find a relation for those for model Owner and field todo', src/libcore/option.rs:1038:5
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
at ChildProcess.<anonymous> (/Users/divyendusingh/Documents/prisma/support/feature/todo-matrix/node_modules/@prisma/prototype/build/index.js:17018:27)
at ChildProcess.emit (events.js:200:13)
at Process.ChildProcess._handle.onexit (internal/child_process.js:272:12) {
query: undefined,
error: undefined,
logs: "thread 'main' panicked at 'Did not find a relation for those for " +
"model Owner and field todo', src/libcore/option.rs:1038:5\nnote: Run " +
'with `RUST_BACKTRACE=1` environment variable to display a backtrace.\n',
isPanicked: undefined
The output should list the dev
command:
โญ Prisma makes your data easy (https://prisma.io)
Usage
$ prisma [command]
Commands
lift Migrate your datamodel
convert Converts a datamodel 1 to datamodel 2
introspect Get the datamodel of your database
generate Generate Photon
new Setup Prisma for your app
seed Seed data into your database
Examples
Initialize files for a new Prisma service
$ prisma new
Deploy service changes (or new service)
$ prisma lift
Idea: Generate .env
file + add it to .gitignore
(needs heuristic).
Describe the bug
React will try to recreate this component tree from scratch using the error boundary you provided, App.
Warning: App: Error boundaries should implement getDerivedStateFromError(). In that method, return a state update to display an error message or fallback UI.
The above error occurred in one of your React components:
in Unknown
in div (created by Box)
in Box
in div (created by Box)
in Box
in div (created by Box)
in Box
in Unknown
in div (created by Box)
in Box
in Unknown
in AppReact will try to recreate this component tree from scratch using the error boundary you provided, App.
Warning: App: Error boundaries should implement getDerivedStateFromError(). In that method, return a state update to display an error message or fallback UI.
To Reproduce
Steps to reproduce the behavior:
Expected behavior
The database select screen
Versions (please complete the following information):
I have this datamodel:
model User {
id: Int @id
name: String
role: Role @default(USER)
posts: Post[]
}
model Post {
id: Int @id
title: String
author: User
}
enum Role {
USER
ADMIN
}
Because role
has a default
value, it can be omitted in the Photon API call:
import { Photon } from '@generated/photon'
const photon = new Photon()
async function main() {
await photon.connect()
const result = await photon.users.create({
data: { name: 'Niko' }
})
console.log(result)
photon.close()
}
main().catch(e => {
console.error(e)
photon.close()
})
When I execute this code, I get the following error:
$ yarn start
yarn run v1.13.0
$ ts-node main.ts
PhotonError: [
{
"error": "ConnectorError(NullConstraintViolation { field_name: \"User.role\" })"
}
]
at NodeEngine.handleErrors (/Users/nikolasburk/Desktop/prisma2-test/node_modules/@generated/photon/runtime/index.js:1308:15)
at /Users/nikolasburk/Desktop/prisma2-test/node_modules/@generated/photon/runtime/index.js:1283:37
at processTicksAndRejections (internal/process/task_queues.js:89:5) {
query: 'mutation {\n createUser(data: {\n ' +
'name: "Niko"\n }) {\n id\n name\n }\n' +
'}',
error: [
{
error: 'ConnectorError(NullConstraintViolation { field_name: "User.role" })'
}
],
logs: '[query-engine/core/src/builders/mutations/root.rs:179] &model.models() = ' +
'[\n Model {\n name: "User",\n stable_identifier: "",\n ' +
' is_embedded: false,\n manifestation: None,\n fields: ' +
'OnceCell {\n once: Once {\n state: Done\n ' +
' },\n value: UnsafeCell\n },\n ' +
'internal_data_model: #InternalDataModelWeakRef#\n },\n Model {\n ' +
' name: "Post",\n stable_identifier: "",\n is_embedded: ' +
'false,\n manifestation: None,\n fields: OnceCell {\n ' +
' once: Once {\n state: Done\n },\n ' +
'value: UnsafeCell\n },\n internal_data_model: ' +
'#InternalDataModelWeakRef#\n }\n]\n' +
'[query-engine/connectors/sql-connector/src/database/sqlite.rs:54] ' +
'visitor::Sqlite::build(q) = (\n "INSERT INTO `migration_engine`.`User` ' +
'(`name`) VALUES (?)",\n [\n Text(\n "Niko"\n )\n ' +
' ]\n)\n',
isPanicked: false
}
โจ Done in 1.25s.
Seeding a database is a process in which a database (usually empty) is populated with an initial set of data. This is often an automated process that is executed upon the initial setup of an application. The data can be dummy data or necessary data such as an initial administrator account.
Use cases:
Considerations:
Prisma1 has a CLI command seed, which reads the configuration from the prisma.yml file. It can execute a seed script written JS.
I installed the SQLite Graphql Boilerplate from the command line and everything works fine until I do the "npm install" which triggers the postinstall script "prisma2 generate".
On Mac I do the exact same with no issues but on Windows 10 I get this error:
Error: write EPIPE
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] postinstall: 'prisma2 generate'
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] postinstall script.
Describe the bug
Error dump:
Error: Error in Photon:
[
{
"error": "ConnectorError(QueryError(Error { kind: ToSql, cause: Some(WrongType(Type(Text))) }\n\nstack backtrace:\n 0: failure::backtrace::internal::InternalBacktrace::new::h84e0252f893b7b0e (0x560f176c75f0)\n 1: <failure::backtrace::Backtrace as core::default::Default>::default::hd8f6c25d93ae9c26 (0x560f176c77a0)\n 2: <sql_connector::error::SqlError as core::convert::From<tokio_postgres::error::Error>>::from::h01dd77df417c902b (0x560f170fa307)\n 3: sql_connector::database::postgresql::<impl sql_connector::transactional::Transaction for postgres::transaction::Transaction>::write::h85ba92f2a437f0c3 (0x560f170cee33)\n 4: sql_connector::transactional::Transaction::insert::hd87cbd91efa25ae3 (0x560f16dbb674)\n 5: sql_connector::transactional::mutaction_executor::create::execute::h75940cff21c312d7 (0x560f1710e305)\n 6: sql_connector::transactional::mutaction_executor::<impl connector::database_mutaction_executor::DatabaseMutactionExecutor for sql_connector::database::SqlDatabase<T>>::execute::{{closure}}::create::hba6453f84f903c89 (0x560f17144268)\n 7: <sql_connector::database::postgresql::PostgreSql as sql_connector::transactional::Transactional>::with_transaction::ha343e63fe0096da4 (0x560f16e2500e)\n 8: sql_connector::transactional::mutaction_executor::<impl connector::database_mutaction_executor::DatabaseMutactionExecutor for sql_connector::database::SqlDatabase<T>>::execute::h76670b1c1482e4fd (0x560f16d83343)\n 9: core::executor::write::WriteQueryExecutor::exec_one::h81acc6e0a7d1cf87 (0x560f171e2382)\n 10: core::executor::write::WriteQueryExecutor::execute::h4de4034d22720586 (0x560f171e1c5e)\n 11: core::executor::Executor::exec_all::h019661aa466552b2 (0x560f171e2e45)\n 12: <prisma::req_handlers::graphql::GraphQlRequestHandler as prisma::req_handlers::RequestHandler>::handle::hd901880e604ba7f3 (0x560f16dff33f)\n 13: prisma::http_handler::h574a96a3df5ad7b2 (0x560f16e46da9)\n 14: <F as actix_web::with::FnWith<T,R>>::call_with::h8921e8f777cf5423 (0x560f16e521be)\n 15: <actix_web::with::WithHandlerFut<T,S,R> as futures::future::Future>::poll::hd0405f55c326258c (0x560f16e5702e)\n 16: actix_web::pipeline::PipelineState<S,H>::poll::h05fe08bd6589e38c (0x560f16d5afe3)\n 17: <actix_web::pipeline::Pipeline<S,H> as actix_web::server::handler::HttpHandlerTask>::poll_io::hf988cbba155aa2c2 (0x560f16d49770)\n 18: actix_web::server::h1::Http1Dispatcher<T,H>::poll_handler::h0e2de2e2f0e85c3b (0x560f16dd599c)\n 19: actix_web::server::h1::Http1Dispatcher<T,H>::poll::h7c512f9208c70f65 (0x560f16e15361)\n 20: <actix_web::server::channel::HttpChannel<T,H> as futures::future::Future>::poll::h3fda956b624b7067 (0x560f16e14079)\n 21: <actix_web::server::channel::HttpChannel<T,H> as futures::future::Future>::poll::h3fda956b624b7067 (0x560f16e15011)\n 22: <actix_net::service::and_then::AndThenFuture<A,B> as futures::future::Future>::poll::hb1978ee7b706a005 (0x560f16ea638d)\n 23: futures::future::chain::Chain<A,B,C>::poll::h4dcce436324a17dd (0x560f16ea07a9)\n 24: futures::task_impl::std::set::hb6a916396ab13c6e (0x560f16f8d892)\n 25: futures::task_impl::Spawn<T>::poll_future_notify::ha13909c8cd98d4fe (0x560f16fa8296)\n 26: tokio_current_thread::CurrentRunner::set_spawn::hcab1a55dd9f5caaf (0x560f16f885a4)\n 27: tokio_current_thread::scheduler::Scheduler<U>::tick::h1f8b6bf060538e24 (0x560f16f94cfa)\n 28: tokio_current_thread::Entered<P>::block_on::hd80cde5490457c04 (0x560f16f88f5a)\n 29: std::thread::local::LocalKey<T>::with::h66e6fccf026a2b83 (0x560f16f9f768)\n 30: std::thread::local::LocalKey<T>::with::h7b6b9e8e24d2b1ae (0x560f16fa0598)\n 31: std::thread::local::LocalKey<T>::with::h4efe62e0c4ca0fb4 (0x560f16f9ea5f)\n 32: std::thread::local::LocalKey<T>::with::h1aefc504c0a5187d (0x560f16f9de55)\n 33: tokio::runtime::current_thread::runtime::Runtime::block_on::h573894b7abc600ef (0x560f16f8c7b0)\n 34: std::sys_common::backtrace::__rust_begin_short_backtrace::h027e6ea9ab4c2090 (0x560f16fa5cb7)\n 35: std::panicking::try::do_call::hbc22e41bfc445835 (0x560f16f8ad66)\n 36: __rust_maybe_catch_panic (0x560f176fb18a)\n at src/libpanic_unwind/lib.rs:87\n 37: core::ops::function::FnOnce::call_once{{vtable.shim}}::hf39704692fc5ef53 (0x560f16fa6556)\n 38: call_once<(),FnBox<()>> (0x560f176e15bf)\n at /rustc/3c235d5600393dfe6c36eeed34042efad8d4f26e/src/liballoc/boxed.rs:702\n 39: call_once<(),alloc::boxed::Box<FnBox<()>>> (0x560f176fa5a0)\n at /rustc/3c235d5600393dfe6c36eeed34042efad8d4f26e/src/liballoc/boxed.rs:702\n start_thread\n at src/libstd/sys_common/thread.rs:14\n thread_start\n at src/libstd/sys/unix/thread.rs:80\n 40: start_thread (0x7fdd3101f57f)\n 41: __clone (0x7fdd30f32f13)\n 42: <unknown> (0x0)))"
}
]
at PhotonFetcher._callee$ (/home/jenkins/code/misc/prisma2_test/generated/photon/index.js:35:23)
at tryCatch (/home/jenkins/code/misc/prisma2_test/node_modules/regenerator-runtime/runtime.js:45:40)
at Generator.invoke [as _invoke] (/home/jenkins/code/misc/prisma2_test/node_modules/regenerator-runtime/runtime.js:271:22)
at Generator.prototype.(anonymous function) [as throw] (/home/jenkins/code/misc/prisma2_test/node_modules/regenerator-runtime/runtime.js:97:21)
at rejected (/home/jenkins/code/misc/prisma2_test/generated/photon/index.js:5:47)
at processTicksAndRejections (internal/process/task_queues.js:86:5)
To Reproduce
Steps to reproduce the behavior:
prisma2 init
and configure with PostgreSQL credentialsdatasource db {
provider = "postgres"
url = "postgresql://test:test@localhost:5432/test?schema=public"
}
generator js {
provider = "photonjs"
output = "./generated/photon"
}
model User {
id String @default(uuid()) @id
email String @unique
name String
}
photon.users()
- works fine (so we are connected)ctx.photon.users.upsert({
create: {
email: '[email protected]',
name: 'Test Testersson'
},
update: {
name: 'Test Terstersson'
},
where: {
email: '[email protected]'
}
});
Expected behavior
I should be able to upsert a user
Versions (please complete the following information):
Postgres
prisma2
CLI: prisma2/0.0.87 (Linux x86_64) node-v11.15.0Zeit/now Version 2 runs on AWS lambdas with the ami ami amzn-ami-hvm-2017.03.rc-1.20170327-x86_64-gp2
.
A binary compiled on that AMI links against the following binaries:
# ldd migration-engine
linux-vdso.so.1 => (0x00007ffd757e8000)
libssl.so.10 => /usr/lib64/libssl.so.10 (0x00007f8fef798000)
libcrypto.so.10 => /lib64/libcrypto.so.10 (0x00007f8fef3b0000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f8fef1ab000)
librt.so.1 => /lib64/librt.so.1 (0x00007f8feefa3000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f8feed87000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f8feeb70000)
libc.so.6 => /lib64/libc.so.6 (0x00007f8fee7ac000)
/lib64/ld-linux-x86-64.so.2 (0x000056492d6bf000)
libm.so.6 => /lib64/libm.so.6 (0x00007f8fee4aa000)
libgssapi_krb5.so.2 => /usr/lib64/libgssapi_krb5.so.2 (0x00007f8fee25b000)
libkrb5.so.3 => /usr/lib64/libkrb5.so.3 (0x00007f8fedf74000)
libcom_err.so.2 => /usr/lib64/libcom_err.so.2 (0x00007f8fedd71000)
libk5crypto.so.3 => /usr/lib64/libk5crypto.so.3 (0x00007f8fedb3e000)
libz.so.1 => /lib64/libz.so.1 (0x00007f8fed928000)
libkrb5support.so.0 => /usr/lib64/libkrb5support.so.0 (0x00007f8fed718000)
libkeyutils.so.1 => /lib64/libkeyutils.so.1 (0x00007f8fed515000)
libresolv.so.2 => /lib64/libresolv.so.2 (0x00007f8fed2fb000)
libselinux.so.1 => /usr/lib64/libselinux.so.1 (0x00007f8fed0d9000)
The specific package versions are found here:
openssl-1.0.1k
glibc-2.17
The binary can be built on an instance with the mentioned ami using the following commands:
yum install git curl
git clone https://github.com/prisma/prisma.git
curl https://sh.rustup.rs -sSf | sh
source $HOME/.cargo/env
yum install openssl-devel
yum groupinstall 'Development Tools'
cd prisma/server/prisma-rs
git checkout alpha
cargo build --release
Project:
intro3.zip
Datamodel
datasource db {
provider = "sqlite"
url = "file:dev.db"
default = true
}
generator photon {
provider = "photon-js"
output = "./generated/photon"
}
model Blog {
id Int @id
name String
viewCount Int
posts Post[]
authors Author[]
}
model Author {
id Int @id
name String?
posts Post[]
blog Blog
}
model Post {
id Int @id
title String
tags String[]
blog Blog
comments Comment[]
}
model Comment {
id Int @id
comment String
}
model Comment {
id Int @id
comment String
}
Two Comment
is definitely an error, but in development often you just want to copy and paste the previous one and override from there. This is probably not an issue with Rust, but rather we need to gracefully recover from these situations in prisma dev
. We may either need to automatically restart the rust engine or not kill the rust engine when these sorts of validation errors happen.
Error
thread 'main' panicked at 'Invariant violation: Inserted key Comment twice, this is a bug and invalidates Weak references. ObjectType { name: "Comment", fields: #Fields Cell# }', query-engine/core/src/schema/builders/cache.rs:62:26
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
/sandbox/node_modules/prisma2/build/index.js:493044
throw ex;
^
SyntaxError: Unexpected end of JSON input
at JSON.parse (<anonymous>)
at child.stdout.d (/sandbox/node_modules/prisma2/build/index.js:96278:25)
at DestroyableTransform.flush [as _flush] (/sandbox/node_modules/prisma2/build/index.js:96293:9)
at DestroyableTransform.prefinish (/sandbox/node_modules/prisma2/build/index.js:173352:10)
at DestroyableTransform.emit (events.js:198:13)
at prefinish (/sandbox/node_modules/prisma2/build/index.js:122896:14)
at finishMaybe (/sandbox/node_modules/prisma2/build/index.js:122905:5)
at endWritable (/sandbox/node_modules/prisma2/build/index.js:122918:3)
at DestroyableTransform.module.exports.3888.Writable.end (/sandbox/node_modules/prisma2/build/index.js:122856:22)
at Socket.onend (_stream_readable.js:655:10)
/cc @Weakky
This feature requests serves as a central place to discuss development and progress for the Neo4j connector.
To Reproduce:
prisma2 introspect
and prisma2 introspect -i
don't go into an interactive mode which i would have expected. Instead it just printed:
Please provide a database name
Right now generators are hardcoded and shipped with the prisma2
CLI. We should open up the generator API to let users bring their own generators (e.g. for prisma-test-utils
) and nexus-prisma
).
Also see: https://gnorm.org
After successfully introspecting MySQL DB, the init wizard terminates without the select Prisma Tools (Photon, Lift), Language, and Boilerplate steps.
The wizard prints the following message after introspecting:
โ Your all set!
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
We created the following files for you:
newapp/prisma/project.prisma The datamodel describes your database schema
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Run the following commands to start developing
$ cd newapp
$ prisma2 dev
Learn more about using Photon and Lift at
https://www.prisma.io/docs/...
Of course, attempting to run prisma2 dev
as directed fails with the following message:
Error
Error: Error in lift engine for rpc applyMigration:
[/root/.cargo/git/checkouts/prisma-query-a8c45647247f5d6d/5a9
5ce6/src/connector/mysql.rs:83] self.pool.state() = State {
connections: 10,
idle_connections: 10,
}
[migration-engine/connectors/sql-migration-connector/src/lib.rs
:273] format!("CREATE SCHEMA IF NOT EXISTS `{}` DEFAULT CHARACTER
SET latin1;" , &
self . schema_name) = "CREATE SCHEMA IF NOT EXISTS `tsh
irtify` DEFAULT CHARACTER SET latin1;"
[/root/.cargo/git/checkouts/prisma-query-a8c45647247f5d6d/5a95c
e6/src/connector/mysql.rs:83] self.pool.state() = State {
connections: 10,
idle_connections: 10,
}
[migration-engine/connectors/sql-migration-connector/src/sql_mi
gration_persistence.rs:38] m.make_from(barrel_variant) = "CREATE T
ABLE tshirtify._Migration (revision INTEGER NOT NULL AUTO_INCREMEN
T PRIMARY KEY, name TEXT NOT NULL, datamodel TEXT NOT NULL, status
TEXT NOT NULL, applied INTEGER NOT NULL, rolled_back INTEGER NOT
NULL, datamodel_steps TEXT NOT NULL, database_migration TEXT NOT N
ULL, errors TEXT NOT NULL, started_at datetime(3) NOT NULL, finish
ed_at datetime(3));"
[/root/.cargo/git/checkouts/prisma-query-a8c45647247f5d6d/5a95c
e6/src/connector/mysql.rs:83] self.pool.state() = State {
connections: 10,
idle_connections: 10,
}
I hope I'm not missing anything.
libssl1.1.so is missing
There already is a GitHub issue asking for a way to submit multiple mutations within the same HTTP request where all mutations are executed as one transaction.
However, this feature does not allow for creating a transaction where the mutations depend on each other. Here is an example where the second operation depends on the first:
prisma.transaction(async tx => {
const user = await tx.createUser({
name,
})
// compute some stuff
await tx.updateOrder(user.id)
await tx.commit()
})
This is currently not possible with the Prisma API as it would require having a long-running connection where the results of the operations are sent back and forth between Prisma and the database.
It should be considered whether it is helpful to add such an API to Prisma, or whether abstractions like nested mutations and the requested transactional execution of multiple independent transactions are the better approach for Prisma.
Consider backward compatibility / breaking changes (e.g. regarding introspection)
How should we handle when running prisma init
in a directory with existing files? Should we also allow for prisma init [new-folder]
?
Let's also see what we can learn from this: https://zeit.co/blog/now-init
Prisma client currently does not provide the ability to retrieve pagination information about a certain query, which prevents developers from accounting for a result setโs size in a variety of cases (display paginated result lists, optimizing batch sizeโฆ)
Add support for a $withPageInfo()
to functions returning collections (findMany
,1-n relations, โฆ) returning pagination information alongside the results.
The pagination information could be described as:
type PageInfo<T> = {
data: T[] // The query result
hasNext: boolean // Indication that the cursor can move to the next page
hasPrev: boolean // Indication that the cursor can move to the previous page
}
Example usage:
const samsPostsWithPageInfo: PageInfo<Post> = await prisma.users
.findOne('sams-id')
.posts({ first: 50 })
.$withPageInfo()
console.log(samsPostsWithPageInfo.hasNext, samsPostsWithPageInfo.hasPrev, samsPostsWithPageInfo.data
In the current workflow, CLI always introspects on non-SQLite databases and does not introspect on SQLite.
We used this classification to provide template downloading for SQLite and introspection for other databases.
The introspection workflow currently does not ask for language, so, we need to print files for either TS/JS or not print anything but prisma.project
file.
This needs further spec.
I suggest the following file/folder layout for a Prisma project:
prisma/
migrations/
20190608154035-base-models/
datamodel.prisma
README.md
steps.json
lift.lock
generated/
photon/
index.js
index.d.ts
.gitignore
project.prisma
Same datamodel/environment as this demo but modified the main.ts
file to
import { Photon } from "@generated/photon";
const photon = new Photon();
async function main() {
await photon.connect();
const result = await photon.blogs.upsert({
where: {
id: 1
},
create: {
name: "Test 1",
viewCount: 1,
posts: {
create: {
title: "X1"
}
}
},
update: {
name: "Test 2",
viewCount: 2,
posts: {
create: {
title: "X2"
}
}
},
select: {
id: true,
posts: {
select: {
id: true
},
first: 10
}
}
});
console.log(result);
photon.close();
}
main().catch(e => {
console.error(e);
photon.close();
});
Running this yields the following error:
$ [p2]$ yarn ts-node main.ts [1/1837]
yarn run v1.15.2
$ /Users/divyendusingh/Documents/prisma/support/feature/p2/node_modules/.bin/ts-node main.ts
{ Error: [
{
"error": "ConnectorError(NullConstraintViolation { field_name: \"Blog.name\" })"
}
]
at NodeEngine.handleErrors (/Users/divyendusingh/Documents/prisma/support/feature/p2/node_modules/@generated/photon/runtime/index.js:1305:15)
at response.json.then.result (/Users/divyendusingh/Documents/prisma/support/feature/p2/node_modules/@generated/photon/runtime/index.js:1280:37)
at process._tickCallback (internal/process/next_tick.js:68:7)
query:
'mutation {\n upsertBlog(\n where: {\n id: 1\n }\n create: {\n name: "Test 1"\n viewCount: 1\n posts: {\n create: [\n {\
n title: "X1"\n }\n ]\n }\n }\n update: {\n name: "Test 2"\n viewCount: 2\n posts: {\n create: [\n {
\n title: "X2"\n }\n ]\n }\n }\n ) {\n id\n name\n viewCount\n posts(first: 10) {\n id\n title\n tags\n
}\n }\n}',
error:
[ { error:
'ConnectorError(NullConstraintViolation { field_name: "Blog.name" })' } ],
logs:
'[query-engine/core/src/schema/builders/input_type_builder/mod.rs:41] format!("{}{}{}Input" , model_name , input_object_name , f . name) = "PostCreatetagsInput"\n[que
ry-engine/core/src/schema/builders/input_type_builder/mod.rs:41] format!("{}{}{}Input" , model_name , input_object_name , f . name) = "PostUpdatetagsInput"\n[query-engin
e/core/src/schema/builders/input_type_builder/mod.rs:41] format!("{}{}{}Input" , model_name , input_object_name , f . name) = "PostUpdatetagsInput"\n[query-engine/core/s
rc/schema/builders/input_type_builder/mod.rs:41] format!("{}{}{}Input" , model_name , input_object_name , f . name) = "PostCreatetagsInput"\n[query-engine/core/src/schem
a/builders/input_type_builder/mod.rs:41] format!("{}{}{}Input" , model_name , input_object_name , f . name) = "PostUpdatetagsInput"\n[query-engine/core/src/schema/builde
rs/input_type_builder/mod.rs:41] format!("{}{}{}Input" , model_name , input_object_name , f . name) = "PostUpdatetagsInput"\n[query-engine/core/src/builders/mutations/ro
ot.rs:179] &model.models() = [\n Model {\n name: "Blog",\n stable_identifier: "",\n is_embedded: false,\n manifestation: None,\n fi
elds: OnceCell {\n once: Once {\n state: Done\n },\n value: UnsafeCell\n },\n internal_data_model: #Interna
lDataModelWeakRef#\n },\n Model {\n name: "Author",\n stable_identifier: "",\n is_embedded: false,\n manifestation: None,\n fiel
ds: OnceCell {\n once: Once {\n state: Done\n },\n value: UnsafeCell\n },\n internal_data_model: #InternalD
ataModelWeakRef#\n },\n Model {\n name: "Post",\n stable_identifier: "",\n is_embedded: false,\n manifestation: None,\n fields:
OnceCell {\n once: Once {\n state: Done\n },\n value: UnsafeCell\n },\n internal_data_model: #InternalDataM
odelWeakRef#\n }\n]\n[query-engine/connectors/sql-connector/src/database/sqlite.rs:63] visitor::Sqlite::build(q) = (\n "SELECT `migration_engine`.`Blog`.`id` FROM
`migration_engine`.`Blog` WHERE `migration_engine`.`Blog`.`id` = ?",\n [\n Integer(\n 1\n )\n ]\n)\n[query-engine/connectors/sql-connector
/src/database/sqlite.rs:54] visitor::Sqlite::build(q) = (\n "INSERT INTO `migration_engine`.`Blog` DEFAULT VALUES",\n []\n)\n',
isPanicked: false }
โจ Done in 1.33s.
To adhere to the spec: https://github.com/prisma/rfcs/blob/0002-datamodel-2/text/0002-datamodel.md#supported-fields
{"id":1,"jsonrpc":"2.0","method":"applyMigration","params":{"projectInfo":"","force":false,"migrationId":"20190622144536-init","steps":[{"stepType":"CreateModel","name":"User","embedded":false},{"stepType":"CreateModel","name":"Post","embedded":false},{"stepType":"CreateField","model":"User","name":"id","type":{"Base":"String"},"arity":"required","isUnique":true,"id":{"strategy":"Auto","sequence":null},"default":{"Expression":["cuid","String",[]]}},{"stepType":"CreateField","model":"User","name":"email","type":{"Base":"String"},"arity":"required","isUnique":true},{"stepType":"CreateField","model":"User","name":"name","type":{"Base":"String"},"arity":"optional","isUnique":false},{"stepType":"CreateField","model":"User","name":"posts","type":{"Relation":{"to":"Post","to_fields":[],"name":"PostToUser","on_delete":"None"}},"arity":"list","isUnique":false},{"stepType":"CreateField","model":"Post","name":"id","type":{"Base":"String"},"arity":"required","isUnique":true,"id":{"strategy":"Auto","sequence":null},"default":{"Expression":["cuid","String",[]]}},{"stepType":"CreateField","model":"Post","name":"createdAt","type":{"Base":"DateTime"},"arity":"required","isUnique":false,"default":{"Expression":["now","DateTime",[]]}},{"stepType":"CreateField","model":"Post","name":"updatedAt","type":{"Base":"DateTime"},"arity":"required","isUnique":false},{"stepType":"CreateField","model":"Post","name":"published","type":{"Base":"Boolean"},"arity":"required","isUnique":false},{"stepType":"CreateField","model":"Post","name":"title","type":{"Base":"String"},"arity":"required","isUnique":false},{"stepType":"CreateField","model":"Post","name":"content","type":{"Base":"String"},"arity":"optional","isUnique":false},{"stepType":"CreateField","model":"Post","name":"author","type":{"Relation":{"to":"User","to_fields":["id"],"name":"PostToUser","on_delete":"None"}},"arity":"optional","isUnique":false}],"sourceConfig":"datasource db {\n provider = \"mysql\"\n url = \"mysql://root:123456789@localhost:3306/prisma\"\n default = true\n}\n\ngenerator photon {\n provider = \"photonjs\"\n output = \"node_modules/@generated/photon\"\n}\n\ngenerator nexus_prisma {\n provider = \"nexus-prisma\"\n output = \"node_modules/@generated/nexus-prisma\"\n}\n\nmodel User {\n id String @default(cuid()) @id @unique\n email String @unique\n name String?\n posts Post[]\n}\n\nmodel Post {\n id String @default(cuid()) @id @unique\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n published Boolean\n title String\n content String?\n author User?\n}"}}
{
"id": 1,
"jsonrpc": "2.0",
"method": "applyMigration",
"params": {
"projectInfo": "",
"force": false,
"migrationId": "20190622144536-init",
"steps": [
{
"stepType": "CreateModel",
"name": "User",
"embedded": false
},
{
"stepType": "CreateModel",
"name": "Post",
"embedded": false
},
{
"stepType": "CreateField",
"model": "User",
"name": "id",
"type": {
"Base": "String"
},
"arity": "required",
"isUnique": true,
"id": {
"strategy": "Auto",
"sequence": null
},
"default": {
"Expression": [
"cuid",
"String",
[]
]
}
},
{
"stepType": "CreateField",
"model": "User",
"name": "email",
"type": {
"Base": "String"
},
"arity": "required",
"isUnique": true
},
{
"stepType": "CreateField",
"model": "User",
"name": "name",
"type": {
"Base": "String"
},
"arity": "optional",
"isUnique": false
},
{
"stepType": "CreateField",
"model": "User",
"name": "posts",
"type": {
"Relation": {
"to": "Post",
"to_fields": [],
"name": "PostToUser",
"on_delete": "None"
}
},
"arity": "list",
"isUnique": false
},
{
"stepType": "CreateField",
"model": "Post",
"name": "id",
"type": {
"Base": "String"
},
"arity": "required",
"isUnique": true,
"id": {
"strategy": "Auto",
"sequence": null
},
"default": {
"Expression": [
"cuid",
"String",
[]
]
}
},
{
"stepType": "CreateField",
"model": "Post",
"name": "createdAt",
"type": {
"Base": "DateTime"
},
"arity": "required",
"isUnique": false,
"default": {
"Expression": [
"now",
"DateTime",
[]
]
}
},
{
"stepType": "CreateField",
"model": "Post",
"name": "updatedAt",
"type": {
"Base": "DateTime"
},
"arity": "required",
"isUnique": false
},
{
"stepType": "CreateField",
"model": "Post",
"name": "published",
"type": {
"Base": "Boolean"
},
"arity": "required",
"isUnique": false
},
{
"stepType": "CreateField",
"model": "Post",
"name": "title",
"type": {
"Base": "String"
},
"arity": "required",
"isUnique": false
},
{
"stepType": "CreateField",
"model": "Post",
"name": "content",
"type": {
"Base": "String"
},
"arity": "optional",
"isUnique": false
},
{
"stepType": "CreateField",
"model": "Post",
"name": "author",
"type": {
"Relation": {
"to": "User",
"to_fields": [
"id"
],
"name": "PostToUser",
"on_delete": "None"
}
},
"arity": "optional",
"isUnique": false
}
],
"sourceConfig": "datasource db {\n provider = \"mysql\"\n url = \"mysql://root:123456789@localhost:3306/prisma\"\n default = true\n}\n\ngenerator photon {\n provider = \"photonjs\"\n output = \"node_modules/@generated/photon\"\n}\n\ngenerator nexus_prisma {\n provider = \"nexus-prisma\"\n output = \"node_modules/@generated/nexus-prisma\"\n}\n\nmodel User {\n id String @default(cuid()) @id @unique\n email String @unique\n name String?\n posts Post[]\n}\n\nmodel Post {\n id String @default(cuid()) @id @unique\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n published Boolean\n title String\n content String?\n author User?\n}"
}
}
null
[/var/root/.cargo/git/checkouts/prisma-query-a8c45647247f5d6d/5a95ce6/src/connector/mysql.rs:83] self.pool.state() = State {
connections: 10,
idle_connections: 10
}
[migration-engine/connectors/sql-migration-connector/src/lib.rs:273] format!("CREATE SCHEMA IF NOT EXISTS `{}` DEFAULT CHARACTER SET latin1;" , &
self . schema_name) = "CREATE SCHEMA IF NOT EXISTS `prisma` DEFAULT CHARACTER SET latin1;"
[/var/root/.cargo/git/checkouts/prisma-query-a8c45647247f5d6d/5a95ce6/src/connector/mysql.rs:83] self.pool.state() = State {
connections: 10,
idle_connections: 10
}
[migration-engine/connectors/sql-migration-connector/src/sql_migration_persistence.rs:38] m.make_from(barrel_variant) = "CREATE TABLE prisma._Migration (revision INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY, name TEXT NOT NULL, datamodel TEXT NOT NULL, status TEXT NOT NULL, applied INTEGER NOT NULL, rolled_back INTEGER NOT NULL, datamodel_steps TEXT NOT NULL, database_migration TEXT NOT NULL, errors TEXT NOT NULL, started_at datetime(3) NOT NULL, finished_at datetime(3));"
[/var/root/.cargo/git/checkouts/prisma-query-a8c45647247f5d6d/5a95ce6/src/connector/mysql.rs:83] self.pool.state() = State {
connections: 10,
idle_connections: 10
}
[/var/root/.cargo/git/checkouts/prisma-query-a8c45647247f5d6d/5a95ce6/src/connector/mysql.rs:83] self.pool.state() = State {
connections: 10,
idle_connections: 10
}
[/var/root/.cargo/git/checkouts/prisma-query-a8c45647247f5d6d/5a95ce6/src/connector/mysql.rs:83] self.pool.state() = State {
connections: 10,
idle_connections: 10
}
[/var/root/.cargo/git/checkouts/prisma-query-a8c45647247f5d6d/5a95ce6/src/connector/mysql.rs:158] visitor::Mysql::build(q) = (
"SELECT `prisma`.`_Migration`.* FROM `prisma`.`_Migration` WHERE `status` = ? ORDER BY `revision` DESC",
[
Text(
"MigrationSuccess"
)
]
)
[/var/root/.cargo/git/checkouts/prisma-query-a8c45647247f5d6d/5a95ce6/src/connector/mysql.rs:83] self.pool.state() = State {
connections: 10,
idle_connections: 10
}
[/var/root/.cargo/git/checkouts/prisma-query-a8c45647247f5d6d/5a95ce6/src/connector/mysql.rs:83] self.pool.state() = State {
connections: 10,
idle_connections: 10
}
[/var/root/.cargo/git/checkouts/prisma-query-a8c45647247f5d6d/5a95ce6/src/connector/mysql.rs:158] visitor::Mysql::build(q) = (
"SELECT `prisma`.`_Migration`.* FROM `prisma`.`_Migration` WHERE `status` = ? ORDER BY `revision` DESC",
[
Text(
"MigrationSuccess"
)
]
)
[/var/root/.cargo/git/checkouts/prisma-query-a8c45647247f5d6d/5a95ce6/src/connector/mysql.rs:83] self.pool.state() = State {
connections: 10,
idle_connections: 10
}
[/var/root/.cargo/git/checkouts/prisma-query-a8c45647247f5d6d/5a95ce6/src/connector/mysql.rs:83] self.pool.state() = State {
connections: 10,
idle_connections: 10
}
[/var/root/.cargo/git/checkouts/prisma-query-a8c45647247f5d6d/5a95ce6/src/connector/mysql.rs:83] self.pool.state() = State {
connections: 10,
idle_connections: 10
}
thread 'main' panicked at 'Error accessing result set, column not found: column_default', migration-engine/connectors/sql-migration-connector/src/database_inspector/information_schema.rs:58:33
stack backtrace:
0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
1: std::sys_common::backtrace::_print
2: std::panicking::default_hook::{{closure}}
3: std::panicking::default_hook
4: std::panicking::rust_panic_with_hook
5: std::panicking::begin_panic
6: core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &mut F>::call_once
7: <alloc::vec::Vec<T> as alloc::vec::SpecExtend<T, I>>::from_iter
8: sql_migration_connector::database_inspector::information_schema::InformationSchema::get_columns
9: sql_migration_connector::database_inspector::mysql_inspector::MysqlInspector::get_table
10: <core::iter::adapters::Map<I, F> as core::iter::traits::iterator::Iterator>::fold
11: <alloc::vec::Vec<T> as alloc::vec::SpecExtend<T, I>>::from_iter
12: <sql_migration_connector::database_inspector::mysql_inspector::MysqlInspector as sql_migration_connector::database_inspector::DatabaseInspector>::introspect
13: <sql_migration_connector::sql_database_migration_inferrer::SqlDatabaseMigrationInferrer as migration_connector::database_migration_inferrer::DatabaseMigrationInferrer<sql_migration_connector::sql_migration::SqlMigration>>::infer
14: migration_core::commands::apply_migration::ApplyMigrationCommand::handle_migration
15: <migration_core::commands::apply_migration::ApplyMigrationCommand as migration_core::commands::command::MigrationCommand>::execute
16: <F as jsonrpc_core::calls::RpcMethodSimple>::call
17: <F as jsonrpc_core::calls::RpcMethod<T>>::call
18: <futures::future::lazy::Lazy<F, R> as futures::future::Future>::poll
19: <futures::future::then::Then<A, B, F> as futures::future::Future>::poll
20: <futures::future::map::Map<A, F> as futures::future::Future>::poll
21: <futures::future::either::Either<A, B> as futures::future::Future>::poll
22: futures::task_impl::std::set
23: <std::thread::local::LocalKey<T>>::with
24: futures::future::Future::wait
25: <jsonrpc_core::io::IoHandler<M>>::handle_request_sync
26: migration_core::rpc_api::RpcApi::handle
27: migration_engine::main
28: std::rt::lang_start::{{closure}}
29: std::panicking::try::do_call
30: __rust_maybe_catch_panic
31: std::rt::lang_start_internal
32: main
In the current GSE post, we mention that multiple files will be generated in the project structure section Expand to learn about the generated project setup.
when From scratch
option is used in the CLI.
This is because the current CLI implements From scratch
differently from other boilerplates by generating the necessary files in memory. However, the spec is to use the example called script
for the respective language.
type Numeric Float @pg.numeric(precision: 5, scale: 2)
@ms.decimal(precision: 5, scale: 2)
gives me this error: Unexpected token. Expected one of: Start of block ("{").
A working example would be nice. (And also more docs on it in general)
Historically prisma introspect
could be used to create a project. Now this functionality has been move into prisma init
. prisma introspect
should now only be used for re-introspection use cases.
Please enforce that prisma introspect
requires a Prisma file.
High-level: create a binary for windows and add it to the Table of binaries
There's very likely more low-level work to do here. Might be worth trying: cargo build --target x86_64-pc-windows-gnu
to see if we get lucky.
javascript
to photonjs
./generated/photon
default output
value + make output
value optionalBased on: https://youtu.be/81PtwbpQEck?t=179
Also: https://cldup.com/XtBQsfFCge.mp4
When pasting the full connection string into the first field, it should be recognized and parsed, the resulting values put into the correct fields for convenience.
The current API doesn't allow to implement application-level Optimistic Concurrency Control, which is a pattern commonly implemented by applications with high concurrency needs, to avoid creating a bottleneck on the database, while obtaining certain guarantees about the integrity of the modified data.
Imagine data that is bound to a given user profile, which only the said user can modify, or their admin. There are few chances that the same user would try to update the same data at the same time, which makes the use of pessimistic locking or long-running transaction solutions suboptimal. Implementing OCC, in this case, will be likely more performant.
OCC assumes that multiple transactions can frequently complete without interfering with each other, hence proposes to not block concurrent transactions and require the application to rollback in case of a detected conflict:
This SO answer gives a great summary
Optimistic Concurrency Control implies reading a record, taking note of a version number (other methods to do this involve dates, timestamps or checksums/hashes), and checking that the version hasn't changed before the record gets written back. When writing the record back, one filters the update on the version to make sure it's atomic. (i.e. hasn't been updated between when checking the version and writing the record to the disk) and update the version in one hit.
If the record is dirty (i.e. has a different version to the user's) the transaction should abort and require the process to be restarted.
PCC is assuming that data will often be accessed by multiple processes at the same time and proposes to lock data to sequence how these processes access it.
In practice, database vendors like Postgres or MySQL implement a concept of table- or row-level locking, giving control over what can be read and written (depending on the transaction's isolation level):
SELECT ... FOR SHARE
which leaves the data available to read, but not to update outside the transaction.SELECT ... FOR UPDATE
which prevents the selected data from being read or updated outside of the transaction.NOWAIT
variant to fail if a row is lockedSKIP LOCKED
variant to skip locked rowsThis approach comes with risk of deadlocks, which can block the application, so should be considered carefully.
While we should consider an API design allowing us to evolve towards supporting and controlling both policies, the first iteration should focus on supporting Optimistic Concurrency Control.
Pessimistic Concurrency Control requires a deeper analysis of combinations of transaction isolation levels along with the locking strategy for each record, which makes it significantly more complex.
The API should offer a way to specify, on update
and in $transaction
calls, whether to check on a certain field used to implement Optimistic Concurrency Control (version, timestamp...).
A single attribute defining the locking policy, so that people can implement Optimistic Concurrency Control:
lockingPolicy: {
optimistic: 'fieldName'
}
This design would possibly allow defining a pessimistic policy in the future:
lockingPolicy: {
pessimistic: 'ROW' // or 'TABLE', depends on database vendor
}
Applied example
Imagine users detaining a number of points in a competition. And that points can be updated based on how each user behaves in the application.
model User {
team Team
numPoints Int
updatedAt DateTime @updatedAt
}
The exchange of points needs to be done in a consistent fashion across users to ensure a fair game.
Pseudo code:
// Optimistic
const scored = 2
prisma.$transaction([
prisma.user.update({
data: {
numPoints: {
decrement: scored
}
},
where: {
id: 'loser-user-id'
},
lockingPolicy: {
optimistic: 'updatedAt'
}
),
prisma.user.update({
data: {
numPoints: {
increment: scored
}
},
where: {
id: 'winner-user-id'
},
lockingPolicy: {
optimistic: 'updatedAt'
}
)
])
// Pessimistic (used to proof the API design, not meant for implementation)
const scored = 2
prisma.$transaction([
prisma.user.update({
data: {
numPoints: {
decrement: scored
}
},
where: {
id: 'loser-user-id'
},
lockingPolicy: {
pessimistic: 'ROW'
}
),
prisma.user.update({
data: {
numPoints: {
increment: scored
}
},
where: {
id: 'winner-user-id'
},
pessimisticLock: {
on: 'ROW'
}
)
], {
isolation: 'REPEATABLE READ'
})
// Optimistic
const scored = 2
const user = prisma.user.findOne({ where: { id: 'loser-user-id' } })
const controlPoints = user.points
try {
// Throws if `numPoints` has changed between the fetching and the update
prisma.user.update({
data: {
numPoints: {
decrement: scored
}
},
where: {
id: 'loser-user-id'
},
if: {
numPoints: controlPoints
}
)
} catch (e) {
// Consider redoing, or error out.
}
for await (const post of prisma.posts().$stream()) {
console.log(post)
}
const postStreamWithPageInfo = await prisma
.posts()
.$stream()
.$withPageInfo()
for await (const posts of prisma.users
.findOne('bobs-id')
.posts({ first: 50 })
.batch({ batchSize: 100 })) {
console.log(posts) // 100 posts
}
// Configure streaming chunkSize and fetchThreshold
prisma.posts({ first: 10000 }).$stream({ chunkSize: 100, fetchThreshold: 0.5 /*, tailable: true*/ })
// Buffering
const posts = await prisma
.posts({ first: 100000 })
.$stream()
.toArray()
// Shortcut for count
const userCount = await prisma.users.count({
where: {
age: {
gt: 18,
},
},
})
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.