-
-
Notifications
You must be signed in to change notification settings - Fork 6.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
sqj.js driver #894
sqj.js driver #894
Conversation
What if we implement following options:
connection.importDatabase(...); // or maybe call it loadDatabase or some better name and
|
Too dirty for users. I would like to avoid adding sqljs-specific methods to connection, however using "any" or any other casting isnt an option. Alternative is to make things like in mongo - create
Probably yes. Alternative is to name it SqlJSDriver, but Im not sure if its better.
we need to include it in ormconfig.dist / ormconfig.travis to run then in nodejs as well. Also probably we'll need to include tests enabled only for this driver which cover this specific functionality (option to auto save or option to provided place)
Im not sure I completely understood this part.... But we need to make things flexible and easy for users. For example user needs to be able to provide simply path, like "/test/db.sql" and it should be automatically handled (for nodejs it may save database in file system and for browser it can save it in local storage). What do you think about it, is it implementable? This does not exclude option to provide utf8array too. |
Thanks for your feedback. I really like your ideas! I will see what I can do |
@daniel-lang any updates on this PR? |
I wasn't comfortable digging that deep into TypeORM, so I waited a bit to get more familiar with TypeORM. I hope, that I can update it this weekend or early next week so that it can be part of the 0.1 release |
… look like This is still missing docs and tests
@pleerock I push the most recent ideas. This is still missing:
|
/** | ||
* True if file or localStorage should be updated automatically | ||
*/ | ||
readonly autoSave?: boolean; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe its better to do boolean|number
and if its a number, its an interval?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would be happen, when it's set to true? Then the interval would have to be a default value, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can make autoSave
to boolean|Function
instead of two parameters
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or maybe even save: boolean|string|Function
where string is a location, what do you think about it?
src/driver/sqljs/SqljsDriver.ts
Outdated
} | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make those methods protected too and put comments
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will add comments, but they are public because I have to call them from the entity manager
this.driver = driver; | ||
this.connection = driver.connection; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public methods are separate block, please try to keep following common code convention
@@ -16,6 +18,9 @@ export class EntityManagerFactory { | |||
if (connection.driver instanceof MongoDriver) | |||
return new MongoEntityManager(connection); | |||
|
|||
// if (connection.driver instanceof SqljsDriver) | |||
// return new SqljsEntityManager(connection, queryRunner); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had to remove these lines becaus otherwise I get a TypeError: Object prototype may only be an Object or null: undefined
error. Which, as far as I can tell, is because require('./EntityManager')
inside SqljsEntityManager
returns {}
. But I don't really know why, the only information I found online was, that it could be a problem with circular dependency. But MongoEntityManager
looks the same and works without problems
.travis.yml
Outdated
@@ -12,6 +12,7 @@ services: | |||
- docker | |||
|
|||
before_script: | |||
- npm install sql.js |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why? we don't do same for other drivers. You just need to install sql.js and save into dependencies
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, you are right
…ft to fix circular dependency
Tests for How do you want to do the autoSave feature?
|
as for me interval does not make lot of sense. Database needs to be saved only after some modifications are applied on it, right? And usually modifications aren't so often, so saving database on that point kinda makes sense. Plus interval isn't reliable - in browser, what if interval is high (and making it low does not make sense because there are lot of reads and much less writes), what if user just closes the window and interval time isn't reached. |
Also we'll need to create a good facade/interface for users to easily configure how it gonna autosave (where?) |
Okay, I will rewrite the autosave part to save after insert, update and delete.
What do you want the user to configure other than enable it and the location (both of them have an option)? |
Do you mean location like |
The way it's implemented right now is:
|
Ah, nice, its implemented already. Then what are things left to be done?:
|
Also, does it have any breaking change? If yes, then PR must be against 0.2.0 branch, if no, then we can merge it directly into master and release this change in 0.1.1 |
src/connection/Connection.ts
Outdated
@@ -128,6 +129,13 @@ export class Connection { | |||
return this.manager as MongoEntityManager; | |||
} | |||
|
|||
get sqljsManager(): SqljsEntityManager { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing comment block
readonly type: "sqljs"; | ||
|
||
/** | ||
* The database definition and data to import |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't forget dots after sentence 🙈
/** | ||
* Sqlite-specific connection options. | ||
*/ | ||
export interface SqljsConnectionOptions extends BaseConnectionOptions { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would improve all comments in this file - make them more concrete and simple for users understanding
readonly autoSaveCallback?: Function; | ||
|
||
/** | ||
* file path or local storage key to load and save database |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Captal + dot
readonly database?: Uint8Array; | ||
|
||
/** | ||
* True if file or localStorage should be updated automatically |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bad comment
src/driver/sqljs/SqljsDriver.ts
Outdated
@@ -0,0 +1,188 @@ | |||
import { AbstractSqliteDriver } from "../sqlite-abstract/AbstractSqliteDriver"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please follow same code conventions everywhere - comment on each property, class, method, dots, etc.
src/driver/sqljs/SqljsQueryRunner.ts
Outdated
} | ||
|
||
if (query.toUpperCase().match(/(COMMIT)/)) { | ||
this.driver.autoSave(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what if autoSave
is an async operation? Callback provided by a user most probably should be a promise since it'll most probably save in the cloud / filesystem / database using async api
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also this code looks a bit like a monkey patch. Isn't it better to do it after save
method call? Plus after operations called by insert/update/delete query builders?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it's a bit of a monkey patch because I didn't want to save after every update/insert/delete, because they are always executed in a transaction, right? So I guess the best place would be commitTransaction()
.
what if autoSave is an async operation?
The question is, do we have to wait for the save operation to complete? What would change?
* that are unique to Sql.js | ||
*/ | ||
export class SqljsEntityManager extends EntityManager { | ||
private driver: SqljsDriver; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use code separation blocks
src/connection/Connection.ts
Outdated
/** | ||
* Gets a sql.js specific Entity Manager that allows to perform special load and save operations | ||
* | ||
* Available only in connection with the sqljs driver |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doooots 😭
The driver is tested in the browser |
test/functional/sqljs/auto-save.ts
Outdated
} | ||
]; | ||
|
||
repository.createQueryBuilder("api").insert().into(Post).values(posts).execute(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only problem left is the autoSave detection. Right now autoSave is called when a transaction is committed or insert()
, update()
or delete()
in the query runner is called and no transaction is active.
But this will not detect bulk inserts like this one as they are executed using the query()
method from query runner.
Any ideas?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe for now you simply call autoSave method of the driver in the case if dirver is sqlitejs right in the insert/update/delete query builders? Now Im not sure about better solution
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if this is that much better than my original "monkey patch" where I checked if the query contains a INSERT
, UPDATE
or DELETE
src/connection/Connection.ts
Outdated
/** | ||
* Creates an Entity Manager for the current connection with the help of the EntityManagerFactory. | ||
*/ | ||
protected createEntityManager(queryRunner?: QueryRunner): EntityManager { | ||
createEntityManager(queryRunner?: QueryRunner): EntityManager { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is the purpose of this method being public? I don't really want to confuse users with both getEntityManager
and createEntityManager
. Plus there is a createQueryRunner which creates an entity as well...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added this method to resolve a circlular dependency. And the EntityManager also uses the method...
src/driver/sqljs/SqljsDriver.ts
Outdated
const content = JSON.stringify(this.databaseConnection.export()); | ||
PlatformTools.getGlobalVariable().localStorage.setItem(path, content); | ||
const database: Uint8Array = this.databaseConnection.export(); | ||
const databaseArray = [].slice.call(database); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what exact [].slice.call(database)
does? copies array? Please add comment for dummies like me who dont know what this specific code does
src/driver/sqljs/SqljsQueryRunner.ts
Outdated
*/ | ||
async commitTransaction(): Promise<void> { | ||
await super.commitTransaction(); | ||
this.driver.autoSave(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
did you miss comment where I told that autoSave most probably should be a promise?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No I didn't.
But I added back then: What would be the difference? I don't know if it would be a good idea to wait for the save operation to complete since it will slow down the driver.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it will be correct to wait a save operation to complete, otherwise users may face unexpected behaviour
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Plus if there will be an error they will see it, it won't disappear in the air
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed that, to wait for save
/** | ||
* Creates an Entity Manager for the current connection with the help of the EntityManagerFactory. | ||
*/ | ||
createEntityManager(queryRunner?: QueryRunner): EntityManager { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the only problem left....I don't know a way around making it public, because I need it in EntityManager
to remove the dependency to EntityManagerFactory
which again depends on EntityManager
. I don't know why this error appeared now, after a added a second special EntityManager...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, lets left it then
…s test as it is already included in other tests
Are we done with this PR and it can be merged? |
I think so! |
Okay, I merged it. Can you please merge master brunch into |
Thank you very much! I'm gonna donate you. |
A driver for sql.js based on the AbstractSqliteDriver.
Two things I'm not quiet sure about yet:
Naming
is there a better way than calling it
SqljsDriver
?Import/Export
Since the database in sql.js is never persisted the library supports an import/export mechanism. The problem with that is, that the database to import has to passed to the constructor in
createDatabaseConnection
. Therefore the only option I could come up with was use an option field (databaseForImport
) which can be set tonew Uint8Array(0)
if nothing should be imported.And with
the database can be exported. Is there a nicer way for the import and maybe even the export?
Tested in the browser, but should also work in node.js
closes #822