From fe20528b177186b2256c765f274d504a1ebe842e Mon Sep 17 00:00:00 2001 From: William Gabriel Pereira Date: Tue, 8 Apr 2025 13:44:35 -0300 Subject: [PATCH 1/5] improv/execQuery legibility --- package.json | 1 + src/orm/query_builder/index.ts | 63 ++++++++++++++++++++-------------- 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/package.json b/package.json index 7ff458998..1997bee9b 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "./commands": "./build/commands/main.js", "./factories": "./build/src/factories/main.js", "./database": "./build/src/database/main.js", + "./query_runner": "./build/src/query_runner/main.js", "./orm": "./build/src/orm/main.js", "./orm/relations": "./build/src/orm/relations/main.js", "./seeders": "./build/src/seeders/main.js", diff --git a/src/orm/query_builder/index.ts b/src/orm/query_builder/index.ts index ca9ac9a8a..a38d08561 100644 --- a/src/orm/query_builder/index.ts +++ b/src/orm/query_builder/index.ts @@ -168,29 +168,15 @@ export class ModelQueryBuilder } } + private isWriteQuery() { + return ['update', 'del', 'insert'].includes((this.knexQuery as any)['_method']) + } + /** - * Executes the current query + * Convert fetched results to an array of model instances */ - private async execQuery() { - this.applyWhere() - - const isWriteQuery = ['update', 'del', 'insert'].includes((this.knexQuery as any)['_method']) - const queryData = Object.assign(this.getQueryData(), this.customReporterData) - const rows = await new QueryRunner(this.client, this.debugQueries, queryData).run( - this.knexQuery - ) - - /** - * Return the rows as it is when query is a write query - */ - if (isWriteQuery || !this.wrapResultsToModelInstances) { - return Array.isArray(rows) ? rows : [rows] - } - - /** - * Convert fetched results to an array of model instances - */ - const modelInstances = rows.reduce((models: LucidRow[], row: ModelObject) => { + private convertRowsToModelInstances(rows: any): LucidRow[] { + return rows.reduce((models: LucidRow[], row: ModelObject) => { if (isObject(row)) { const modelInstance = this.model.$createFromAdapterResult( row, @@ -209,14 +195,39 @@ export class ModelQueryBuilder } return models }, []) + } - /** - * Preload for model instances - */ - await this.preloader + /** + * Preload for model instances + */ + private async preloadFromModels(models: LucidRow[]): Promise { + return this.preloader .sideload(this.sideloaded) .debug(this.debugQueries) - .processAllForMany(modelInstances, this.client) + .processAllForMany(models, this.client) + } + + /** + * Executes the current query + */ + private async execQuery() { + this.applyWhere() + + const queryData = Object.assign(this.getQueryData(), this.customReporterData) + const rows = await new QueryRunner(this.client, this.debugQueries, queryData).run( + this.knexQuery + ) + + /** + * Return the rows as it is when query is a write query + */ + if (this.isWriteQuery() || !this.wrapResultsToModelInstances) { + return Array.isArray(rows) ? rows : [rows] + } + + const modelInstances = this.convertRowsToModelInstances(rows) + + await this.preloadFromModels(modelInstances) return modelInstances } From 239ab23e429af79d8aabb9c1e12929574836bfaf Mon Sep 17 00:00:00 2001 From: William Gabriel Pereira Date: Tue, 3 Jun 2025 08:20:09 -0300 Subject: [PATCH 2/5] Creating tests to new execQuery --- .env | 2 +- src/orm/query_builder/index.ts | 6 +- .../model_query_builder_query_exec.spec.ts | 173 ++++++++++++++++++ 3 files changed, 177 insertions(+), 4 deletions(-) create mode 100644 test/orm/model_query_builder_query_exec.spec.ts diff --git a/.env b/.env index 3e9e75cb2..721fa0c44 100644 --- a/.env +++ b/.env @@ -13,7 +13,7 @@ MYSQL_USER=virk PG_HOST=localhost PG_PASSWORD=secret PG_DATABASE=lucid -PG_PORT=5432 +PG_PORT=5435 PG_USER=virk MSSQL_HOST=localhost diff --git a/src/orm/query_builder/index.ts b/src/orm/query_builder/index.ts index a38d08561..9c4bac818 100644 --- a/src/orm/query_builder/index.ts +++ b/src/orm/query_builder/index.ts @@ -168,14 +168,14 @@ export class ModelQueryBuilder } } - private isWriteQuery() { + public isWriteQuery() { return ['update', 'del', 'insert'].includes((this.knexQuery as any)['_method']) } /** * Convert fetched results to an array of model instances */ - private convertRowsToModelInstances(rows: any): LucidRow[] { + public convertRowsToModelInstances(rows: any): LucidRow[] { return rows.reduce((models: LucidRow[], row: ModelObject) => { if (isObject(row)) { const modelInstance = this.model.$createFromAdapterResult( @@ -200,7 +200,7 @@ export class ModelQueryBuilder /** * Preload for model instances */ - private async preloadFromModels(models: LucidRow[]): Promise { + public async preloadFromModels(models: LucidRow[]): Promise { return this.preloader .sideload(this.sideloaded) .debug(this.debugQueries) diff --git a/test/orm/model_query_builder_query_exec.spec.ts b/test/orm/model_query_builder_query_exec.spec.ts new file mode 100644 index 000000000..069e32497 --- /dev/null +++ b/test/orm/model_query_builder_query_exec.spec.ts @@ -0,0 +1,173 @@ +/* + * @adonisjs/lucid + * + * (c) Harminder Virk + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import { test } from '@japa/runner' +import { AppFactory } from '@adonisjs/core/factories/app' + +import { column, hasMany } from '../../src/orm/decorators/index.js' +import { ModelQueryBuilder } from '../../src/orm/query_builder/index.js' +import { + getDb, + setup, + cleanup, + ormAdapter, + resetTables, + getBaseModel, +} from '../../test-helpers/index.js' +import type { HasMany } from '../../src/types/relations.js' +import { base64 } from '@poppinss/utils' +import { QueryRunner } from '../../src/query_runner/index.js' +import { FileSystem } from '@japa/file-system' +import { LucidRow } from '../../src/types/model.js' + +const setupCache = async (fs: FileSystem) => { + const fakeCache = new Map() + + const app = new AppFactory().create(fs.baseUrl, () => {}) + await app.init() + const db = getDb() + const adapter = ormAdapter(db) + const BaseModel = getBaseModel(adapter) + + class Post extends BaseModel { + @column() + declare userId: number | null + + @column() + declare title: string + } + + class User extends BaseModel { + @column({ isPrimary: true }) + declare id: number + + @column() + declare username: string + + @hasMany(() => Post) + declare posts: HasMany + } + + Post.boot() + User.boot() + + ModelQueryBuilder.macro( + 'execQuery' as keyof ModelQueryBuilder, + async function (this: ModelQueryBuilder) { + this.applyWhere() + const isWriteQuery = this.isWriteQuery() + + const cacheTags = [this.model.table] + cacheTags.push(base64.urlEncode(this.toQuery())) + + if (!isWriteQuery) { + const cacheKey = cacheTags.join(':') + let cachedData = fakeCache.get(cacheKey) + + if (cachedData) { + let data = cachedData + + if (!Array.isArray(data)) data = [data] + + if (this.wrapResultsToModelInstances) { + return this.convertRowsToModelInstances(data) + } else { + return data + } + } + } + + const queryData = Object.assign(this.getQueryData(), this.customReporterData) + const rows = await new QueryRunner(this.client, this.debugQueries, queryData).run( + this.knexQuery + ) + + if (isWriteQuery || !this.wrapResultsToModelInstances) { + if (isWriteQuery) { + cacheTags.pop() + const cacheKey = cacheTags.join(':') + fakeCache.forEach((_, key) => { + if (key.startsWith(cacheKey)) { + fakeCache.delete(key) + } + }) + } + return Array.isArray(rows) ? rows : [rows] + } + + const modelInstances = this.convertRowsToModelInstances(rows) + + if (!isWriteQuery) { + const cacheKey = cacheTags.join(':') + fakeCache.set(cacheKey, modelInstances) + } + + await this.preloadFromModels(modelInstances) + + return modelInstances + } + ) + + return { + fakeCache, + Post, + User, + } +} + +test.group('Model query builder execQuery', (group) => { + group.setup(async () => { + await setup() + }) + + group.teardown(async () => { + await cleanup() + }) + + group.each.teardown(async () => { + await resetTables() + }) + + group.each.disableTimeout() + + test('apply relationship constraints when using sub query', async ({ fs, assert }) => { + const { fakeCache, Post, User } = await setupCache(fs) + + const users = await User.createMany([ + { + username: 'virk', + }, + { + username: 'nikk', + }, + ]) + + assert.lengthOf(fakeCache, 0) + + for (let user of users) { + await user.related('posts').create({ title: 'Test' }) + } + + const postsQuery = Post.query().whereIn('id', users[0].related('posts').query().select('id')) + + const posts = await postsQuery + + assert.lengthOf(fakeCache, 1) + + const newPosts = await postsQuery + + assert.lengthOf(fakeCache, 1) + assert.deepEqual(posts, newPosts) + + await users[0].related('posts').create({ title: 'Test 2' }) + + // It breaks here, because related().create doesn't call execQuery, so it don't clears the cache + assert.lengthOf(fakeCache, 0) + }) +}) From b37e132fb5f7faa4a3dcad8e1241dd3f3f183897 Mon Sep 17 00:00:00 2001 From: William Roger Dumes Date: Tue, 10 Jun 2025 17:27:49 -0300 Subject: [PATCH 3/5] test: improv new testes in Model QueryBuilder execQuery --- .env | 4 +- .../model_query_builder_query_exec.spec.ts | 357 ++++++++++++++++-- 2 files changed, 333 insertions(+), 28 deletions(-) diff --git a/.env b/.env index 721fa0c44..da434f4a8 100644 --- a/.env +++ b/.env @@ -7,13 +7,13 @@ LEGACY_MYSQL_USER=virk MYSQL_HOST=localhost MYSQL_PASSWORD=secret MYSQL_DATABASE=lucid -MYSQL_PORT=3308 +MYSQL_PORT=3310 MYSQL_USER=virk PG_HOST=localhost PG_PASSWORD=secret PG_DATABASE=lucid -PG_PORT=5435 +PG_PORT=5436 PG_USER=virk MSSQL_HOST=localhost diff --git a/test/orm/model_query_builder_query_exec.spec.ts b/test/orm/model_query_builder_query_exec.spec.ts index 069e32497..80ac93cbb 100644 --- a/test/orm/model_query_builder_query_exec.spec.ts +++ b/test/orm/model_query_builder_query_exec.spec.ts @@ -10,7 +10,13 @@ import { test } from '@japa/runner' import { AppFactory } from '@adonisjs/core/factories/app' -import { column, hasMany } from '../../src/orm/decorators/index.js' +import { + afterDelete, + afterSave, + belongsTo, + column, + hasMany, +} from '../../src/orm/decorators/index.js' import { ModelQueryBuilder } from '../../src/orm/query_builder/index.js' import { getDb, @@ -20,7 +26,7 @@ import { resetTables, getBaseModel, } from '../../test-helpers/index.js' -import type { HasMany } from '../../src/types/relations.js' +import type { BelongsTo, HasMany } from '../../src/types/relations.js' import { base64 } from '@poppinss/utils' import { QueryRunner } from '../../src/query_runner/index.js' import { FileSystem } from '@japa/file-system' @@ -35,15 +41,36 @@ const setupCache = async (fs: FileSystem) => { const adapter = ormAdapter(db) const BaseModel = getBaseModel(adapter) - class Post extends BaseModel { + class Cacheable extends BaseModel { + private static clearCache(model: typeof Cacheable) { + const cacheTags = [(model.constructor as typeof Cacheable).table] + const cacheKey = cacheTags.join(':') + fakeCache.forEach((_, key) => { + if (key.startsWith(cacheKey)) { + fakeCache.delete(key) + } + }) + } + + @afterSave() + static afterSaveCache = Cacheable.clearCache + + @afterDelete() + static afterDeleteCache = Cacheable.clearCache + } + + class Post extends Cacheable { @column() declare userId: number | null @column() declare title: string + + @belongsTo(() => User) + declare user: BelongsTo } - class User extends BaseModel { + class User extends Cacheable { @column({ isPrimary: true }) declare id: number @@ -61,7 +88,10 @@ const setupCache = async (fs: FileSystem) => { 'execQuery' as keyof ModelQueryBuilder, async function (this: ModelQueryBuilder) { this.applyWhere() - const isWriteQuery = this.isWriteQuery() + const isWhereExists = (this as any).whereStack.find((wheres: [any]) => + wheres.find((where: any) => where.method === 'whereExists') + ) + const isWriteQuery = isWhereExists || this.isWriteQuery() const cacheTags = [this.model.table] cacheTags.push(base64.urlEncode(this.toQuery())) @@ -88,22 +118,23 @@ const setupCache = async (fs: FileSystem) => { this.knexQuery ) - if (isWriteQuery || !this.wrapResultsToModelInstances) { - if (isWriteQuery) { - cacheTags.pop() - const cacheKey = cacheTags.join(':') - fakeCache.forEach((_, key) => { - if (key.startsWith(cacheKey)) { - fakeCache.delete(key) - } - }) - } + if (isWriteQuery) { + cacheTags.pop() + const cacheKey = cacheTags.join(':') + fakeCache.forEach((_, key) => { + if (key.startsWith(cacheKey)) { + fakeCache.delete(key) + } + }) + } + + if ((isWriteQuery && !isWhereExists) || !this.wrapResultsToModelInstances) { return Array.isArray(rows) ? rows : [rows] } const modelInstances = this.convertRowsToModelInstances(rows) - if (!isWriteQuery) { + if (!isWriteQuery || isWhereExists) { const cacheKey = cacheTags.join(':') fakeCache.set(cacheKey, modelInstances) } @@ -121,7 +152,7 @@ const setupCache = async (fs: FileSystem) => { } } -test.group('Model query builder execQuery', (group) => { +test.group('Model QueryBuilder execQuery', (group) => { group.setup(async () => { await setup() }) @@ -139,14 +170,7 @@ test.group('Model query builder execQuery', (group) => { test('apply relationship constraints when using sub query', async ({ fs, assert }) => { const { fakeCache, Post, User } = await setupCache(fs) - const users = await User.createMany([ - { - username: 'virk', - }, - { - username: 'nikk', - }, - ]) + const users = await User.createMany([{ username: 'virk' }, { username: 'nikk' }]) assert.lengthOf(fakeCache, 0) @@ -166,8 +190,289 @@ test.group('Model query builder execQuery', (group) => { assert.deepEqual(posts, newPosts) await users[0].related('posts').create({ title: 'Test 2' }) + assert.lengthOf(fakeCache, 0) + + await users[0].load('posts') + assert.lengthOf(fakeCache, 1) + + await users[0].load('posts') + assert.lengthOf(fakeCache, 1) + + const userFirst = await User.query().preload('posts').firstOrFail() + assert.lengthOf(fakeCache, 3) + + const userLast = await User.query().preload('posts').where('id', userFirst.id).firstOrFail() + assert.lengthOf(fakeCache, 4) + assert.deepEqual(userFirst.posts, userLast.posts) + + await userLast.related('posts').create({ title: 'Test 3' }) + assert.lengthOf(fakeCache, 2) + }) + + test('caches a simple query and reuses the result', async ({ fs, assert }) => { + const { fakeCache, User } = await setupCache(fs) + + await User.create({ username: 'virk' }) + + const firstCall = await User.query().where('username', 'virk') + assert.lengthOf(firstCall, 1) + assert.lengthOf(fakeCache, 1) + + const secondCall = await User.query().where('username', 'virk') + assert.lengthOf(secondCall, 1) + assert.lengthOf(fakeCache, 1) + + assert.deepEqual(firstCall, secondCall) + }) + + test('clears cache after deleting a record', async ({ fs, assert }) => { + const { fakeCache, User } = await setupCache(fs) + + const user = await User.create({ username: 'virk' }) + + await User.query().where('username', 'virk') + assert.lengthOf(fakeCache, 1) - // It breaks here, because related().create doesn't call execQuery, so it don't clears the cache + await user.delete() assert.lengthOf(fakeCache, 0) }) + + test('preload does not create duplicate cache entries', async ({ fs, assert }) => { + const { fakeCache, User } = await setupCache(fs) + + const user = await User.create({ username: 'virk' }) + await user.related('posts').create({ title: 'Test' }) + + await User.query().preload('posts') + assert.lengthOf(fakeCache, 2) + + await User.query().preload('posts') + assert.lengthOf(fakeCache, 2) + }) + + test('distinct queries create separate cache entries', async ({ fs, assert }) => { + const { fakeCache, User } = await setupCache(fs) + + await User.createMany([{ username: 'virk' }, { username: 'nikk' }]) + + await User.query().where('username', 'virk') + await User.query().where('username', 'nikk') + + assert.lengthOf(fakeCache, 2) + }) + + test('write queries do not use cache and invalidate it', async ({ fs, assert }) => { + const { fakeCache, User } = await setupCache(fs) + + await User.create({ username: 'virk' }) + + await User.query().where('username', 'virk') + assert.lengthOf(fakeCache, 1) + + await User.query().where('username', 'virk').update({ username: 'updated' }) + assert.lengthOf(fakeCache, 0) + }) + + test('cache is invalidated on update', async ({ fs, assert }) => { + const { fakeCache, User } = await setupCache(fs) + + await User.create({ username: 'virk' }) + assert.equal(fakeCache.size, 0) + + const userQuery = User.query().where('username', 'virk') + + const user = await userQuery.firstOrFail() + assert.equal(fakeCache.size, 1) + + await userQuery.firstOrFail() + assert.equal(fakeCache.size, 1) + + user.username = 'virk-updated' + await user.save() + assert.equal(fakeCache.size, 0) + + const updatedUser = await User.query().where('username', 'virk-updated').first() + assert.isNotNull(updatedUser) + assert.equal(updatedUser!.username, 'virk-updated') + assert.equal(fakeCache.size, 1) + }) + + test('cache is invalidated on delete', async ({ fs, assert }) => { + const { fakeCache, User } = await setupCache(fs) + + const user = await User.create({ username: 'ephemere' }) + assert.equal(fakeCache.size, 0) + + const userQuery = User.query().where('username', 'ephemere') + + await userQuery.firstOrFail() + assert.equal(fakeCache.size, 1) + + await user.delete() + assert.equal(fakeCache.size, 0) + + const deletedUser = await userQuery.first() + assert.isNull(deletedUser) + assert.equal(fakeCache.size, 1) + }) + + test('different queries create different cache entries', async ({ fs, assert }) => { + const { fakeCache, User } = await setupCache(fs) + + await User.createMany([{ username: 'one' }, { username: 'two' }]) + assert.equal(fakeCache.size, 0) + + await User.query().where('username', 'one').first() + assert.equal(fakeCache.size, 1) + + await User.query().where('username', 'two').first() + assert.equal(fakeCache.size, 2) + + await User.all() + assert.equal(fakeCache.size, 3) + + await User.query().where('username', 'one').first() + assert.equal(fakeCache.size, 3) + }) + + test('paginated queries are cached separately', async ({ fs, assert }) => { + const { fakeCache, User } = await setupCache(fs) + + await User.createMany(Array.from({ length: 5 }, (_, i) => ({ username: `user${i + 1}` }))) + assert.equal(fakeCache.size, 0) + + await User.query().forPage(1, 2) + assert.equal(fakeCache.size, 1) + + await User.query().forPage(2, 2) + assert.equal(fakeCache.size, 2) + + await User.query().forPage(1, 2) + assert.equal(fakeCache.size, 2) + }) + + test('aggregate queries do not interact with model instance cache', async ({ fs, assert }) => { + const { fakeCache, User } = await setupCache(fs) + + await User.createMany([{ username: 'a' }, { username: 'b' }]) + assert.equal(fakeCache.size, 0) + + await User.all() + assert.equal(fakeCache.size, 1) + + const count = await User.query().count('* as total') + assert.equal(count[0].$extras.total, 2) + + assert.equal(fakeCache.size, 2) + }) + + test('caches separate entries for queries with different orderBy clauses', async ({ + fs, + assert, + }) => { + const { fakeCache, User } = await setupCache(fs) + + await User.createMany([{ username: 'b' }, { username: 'a' }]) + assert.equal(fakeCache.size, 0) + + await User.query().orderBy('username', 'asc') + assert.equal(fakeCache.size, 1) + + await User.query().orderBy('username', 'desc') + assert.equal(fakeCache.size, 2) + }) + + test('caches query with multiple preloads separately', async ({ fs, assert }) => { + const { fakeCache, User } = await setupCache(fs) + + const user = await User.create({ username: 'multi' }) + await user.related('posts').create({ title: 'Post' }) + + await User.query().preload('posts') + assert.equal(fakeCache.size, 2) + + await User.query().preload('posts') + assert.equal(fakeCache.size, 2) + + await User.query().preload('posts') + assert.equal(fakeCache.size, 2) + }) + + test('queries with joins do not reuse the same cache as simple queries', async ({ + fs, + assert, + }) => { + const { fakeCache, User } = await setupCache(fs) + + const user = await User.create({ username: 'withJoin' }) + await user.related('posts').create({ title: 'Join Post' }) + + await User.query().where('username', 'withJoin') + assert.equal(fakeCache.size, 1) + + await User.query().innerJoin('posts', 'users.id', 'posts.user_id').select('users.*') + assert.equal(fakeCache.size, 2) + }) + + test('caches results from whereHas queries', async ({ fs, assert }) => { + const { fakeCache, User, Post } = await setupCache(fs) + + const user = await User.create({ username: 'related' }) + + await user.related('posts').create({ title: 'Has Post' }) + const newPost = await user.related('posts').create({ title: 'Delete Post' }) + + await User.query() + .preload('posts') + .whereHas('posts', () => {}) + .firstOrFail() + assert.equal(fakeCache.size, 2) + + await User.query() + .preload('posts') + .whereHas('posts', () => {}) + .firstOrFail() + assert.equal(fakeCache.size, 2) + + await Post.query().where('userId', newPost.userId!).where('title', newPost.title!).delete() + assert.equal(fakeCache.size, 1) + + const userHas = await User.query() + .whereHas('posts', () => {}) + .preload('posts') + .firstOrFail() + assert.equal(fakeCache.size, 2) + + const correctUser = await User.query().where('id', userHas.id).preload('posts').firstOrFail() + assert.deepEqual(userHas.posts, correctUser.posts) + assert.equal(fakeCache.size, 3) + + await user.related('posts').create({ title: 'New Post' }) + assert.equal(fakeCache.size, 2) + }) + + test('bulk deletes invalidate all relevant cache entries', async ({ fs, assert }) => { + const { fakeCache, User } = await setupCache(fs) + + await User.createMany([{ username: 'bulk1' }, { username: 'bulk2' }]) + + await User.query().where('username', 'bulk1') + await User.query().where('username', 'bulk2') + assert.equal(fakeCache.size, 2) + + await User.query().delete() + assert.equal(fakeCache.size, 0) + }) + + test('calling refresh', async ({ fs, assert }) => { + const { fakeCache, User } = await setupCache(fs) + + const user = await User.create({ username: 'refreshable' }) + + await User.query().where('username', 'refreshable').first() + assert.equal(fakeCache.size, 1) + + await user.refresh() + assert.equal(fakeCache.size, 2) + }) }) From b351d12a1bd891c7b54659bcda551c0bf39bbc26 Mon Sep 17 00:00:00 2001 From: William Roger Dumes Date: Mon, 16 Jun 2025 09:41:40 -0300 Subject: [PATCH 4/5] refactor: cancel env bd port --- .env | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.env b/.env index da434f4a8..721fa0c44 100644 --- a/.env +++ b/.env @@ -7,13 +7,13 @@ LEGACY_MYSQL_USER=virk MYSQL_HOST=localhost MYSQL_PASSWORD=secret MYSQL_DATABASE=lucid -MYSQL_PORT=3310 +MYSQL_PORT=3308 MYSQL_USER=virk PG_HOST=localhost PG_PASSWORD=secret PG_DATABASE=lucid -PG_PORT=5436 +PG_PORT=5435 PG_USER=virk MSSQL_HOST=localhost From b230b22a15e440632ffc6b147ab1c0ad667d91aa Mon Sep 17 00:00:00 2001 From: William Roger Dumes Date: Mon, 16 Jun 2025 09:43:24 -0300 Subject: [PATCH 5/5] refactor: cancel env pg port --- .env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env b/.env index 721fa0c44..3e9e75cb2 100644 --- a/.env +++ b/.env @@ -13,7 +13,7 @@ MYSQL_USER=virk PG_HOST=localhost PG_PASSWORD=secret PG_DATABASE=lucid -PG_PORT=5435 +PG_PORT=5432 PG_USER=virk MSSQL_HOST=localhost