Browse Source

Merge pull request #3953 from nocodb/fix/3696-save-new-row-with-rqd-fld

Fix: Grid view - save new row only when all required cell values provided
pull/3996/head
Pranav C 2 years ago committed by GitHub
parent
commit
5937b0a010
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 80
      packages/nc-gui/components/smartsheet/Grid.vue
  2. 4
      packages/nc-gui/components/smartsheet/expanded-form/Header.vue
  3. 3
      packages/nc-gui/components/smartsheet/header/VirtualCell.vue
  4. 85
      packages/nc-gui/composables/useExpandedFormStore.ts
  5. 18
      packages/nc-gui/composables/useSmartsheetRowStore.ts
  6. 50
      packages/nc-gui/composables/useViewData.ts
  7. 15
      packages/nc-gui/utils/columnUtils.ts
  8. 57
      packages/nc-gui/utils/dataUtils.ts
  9. 281
      packages/nocodb-sdk/package-lock.json
  10. 2
      packages/nocodb/tests/pg-sakila-db/01-postgres-sakila-schema.sql

80
packages/nc-gui/components/smartsheet/Grid.vue

@ -1,5 +1,5 @@
<script lang="ts" setup>
import type { ColumnType } from 'nocodb-sdk'
import type { ColumnType, TableType, ViewType } from 'nocodb-sdk'
import { UITypes, isVirtualCol } from 'nocodb-sdk'
import {
ActiveViewInj,
@ -18,6 +18,7 @@ import {
createEventHook,
extractPkFromRow,
inject,
isColumnRequiredAndNull,
message,
onClickOutside,
onMounted,
@ -127,15 +128,6 @@ provide(CellUrlDisableOverlayInj, disableUrlOverlay)
const showLoading = ref(true)
reloadViewDataHook?.on(async (shouldShowLoading) => {
// set value if spinner should be hidden
showLoading.value = !!shouldShowLoading
await loadData()
// reset to default (showing spinner on load)
showLoading.value = true
})
const skipRowRemovalOnCancel = ref(false)
const expandForm = (row: Row, state?: Record<string, any>, fromToolbar = false) => {
@ -156,11 +148,6 @@ const expandForm = (row: Row, state?: Record<string, any>, fromToolbar = false)
}
}
openNewRecordFormHook?.on(async () => {
const newRow = await addEmptyRow()
expandForm(newRow, undefined, true)
})
const onresize = (colID: string, event: any) => {
updateWidth(colID, event.detail)
}
@ -271,30 +258,54 @@ const showContextMenu = (e: MouseEvent, target?: { row: number; col: number }) =
const rowRefs = $ref<any[]>()
/** save/update records before unmounting the component */
onBeforeUnmount(async () => {
const saveOrUpdateRecords = async (args: { metaValue?: TableType; viewMetaValue?: ViewType; data?: any } = {}) => {
let index = -1
for (const currentRow of data.value) {
for (const currentRow of args.data || data.value) {
index++
/** if new record save row and save the LTAR cells */
if (currentRow.rowMeta.new) {
const syncLTARRefs = rowRefs[index]!.syncLTARRefs
const savedRow = await updateOrSaveRow(currentRow, '')
await syncLTARRefs(savedRow)
const savedRow = await updateOrSaveRow(currentRow, '', {}, args)
await syncLTARRefs(savedRow, args)
currentRow.rowMeta.changed = false
continue
}
/** if existing row check updated cell and invoke update method */
if (currentRow.rowMeta.changed) {
currentRow.rowMeta.changed = false
for (const field of meta.value?.columns ?? []) {
for (const field of (args.metaValue || meta.value)?.columns ?? []) {
if (isVirtualCol(field)) continue
if (currentRow.row[field.title!] !== currentRow.oldRow[field.title!]) {
await updateOrSaveRow(currentRow, field.title!)
await updateOrSaveRow(currentRow, field.title!, args)
}
}
}
}
}
async function reloadViewDataHandler(shouldShowLoading: boolean | void) {
// set value if spinner should be hidden
showLoading.value = !!shouldShowLoading
await loadData()
// reset to default (showing spinner on load)
showLoading.value = true
}
async function openNewRecordHandler() {
const newRow = await addEmptyRow()
expandForm(newRow, undefined, true)
}
reloadViewDataHook?.on(reloadViewDataHandler)
openNewRecordFormHook?.on(openNewRecordHandler)
onBeforeUnmount(() => {
/** save/update records before unmounting the component */
saveOrUpdateRecords()
// reset hooks
reloadViewDataHook?.off(reloadViewDataHandler)
openNewRecordFormHook?.off(openNewRecordHandler)
})
const expandedFormOnRowIdDlg = computed({
@ -319,9 +330,21 @@ provide(ReloadRowDataHookInj, reloadViewDataHook)
// reloadViewDataHook.trigger()
watch(
() => view.value?.id,
view,
async (next, old) => {
if (next && next !== old) {
if (next && next.id !== old?.id) {
// whenever tab changes or view changes save any unsaved data
if (old?.id) {
const { getMeta } = useMetas()
const oldMeta = await getMeta(old.fk_model_id!)
if (oldMeta) {
await saveOrUpdateRecords({
viewMetaValue: old,
metaValue: oldMeta as TableType,
data: data.value,
})
}
}
await loadData()
}
},
@ -478,9 +501,10 @@ watch([() => selected.row, () => selected.col], ([row, col]) => {
:key="columnObj.id"
class="cell relative cursor-pointer nc-grid-cell"
:class="{
active:
'active':
(isUIAllowed('xcDatatableEditable') && selected.col === colIndex && selected.row === rowIndex) ||
(isUIAllowed('xcDatatableEditable') && selectedRange(rowIndex, colIndex)),
'nc-required-cell': isColumnRequiredAndNull(columnObj, row.row),
}"
:data-key="rowIndex + columnObj.id"
:data-col="columnObj.id"
@ -509,7 +533,7 @@ watch([() => selected.row, () => selected.col], ([row, col]) => {
:row-index="rowIndex"
:active="selected.col === colIndex && selected.row === rowIndex"
@update:edit-enabled="editEnabled = false"
@save="updateOrSaveRow(row, columnObj.title)"
@save="updateOrSaveRow(row, columnObj.title, state)"
@navigate="onNavigate"
@cancel="editEnabled = false"
/>
@ -707,4 +731,8 @@ watch([() => selected.row, () => selected.col], ([row, col]) => {
tbody tr:hover {
@apply bg-gray-100 bg-opacity-50;
}
.nc-required-cell {
box-shadow: inset 0 0 2px #f00;
}
</style>

4
packages/nc-gui/components/smartsheet/expanded-form/Header.vue

@ -19,7 +19,7 @@ const { meta, isSqlView } = useSmartsheetStoreOrThrow()
const { commentsDrawer, primaryValue, primaryKey, save: _save, loadRow } = useExpandedFormStoreOrThrow()
const { isNew, syncLTARRefs } = useSmartsheetRowStoreOrThrow()
const { isNew, syncLTARRefs, state } = useSmartsheetRowStoreOrThrow()
const { isUIAllowed } = useUIPermission()
@ -27,7 +27,7 @@ const reloadTrigger = inject(ReloadRowDataHookInj, createEventHook())
const save = async () => {
if (isNew.value) {
const data = await _save()
const data = await _save(state.value)
await syncLTARRefs(data)
reloadTrigger?.trigger()
} else {

3
packages/nc-gui/components/smartsheet/header/VirtualCell.vue

@ -7,6 +7,7 @@ import {
MetaInj,
computed,
inject,
isVirtualColRequired,
provide,
ref,
toRef,
@ -108,7 +109,7 @@ const tooltipMsg = computed(() => {
<span class="name" style="white-space: nowrap" :title="column.title"> {{ column.title }}</span>
</a-tooltip>
<span v-if="column.rqd || required" class="text-red-500">&nbsp;*</span>
<span v-if="isVirtualColRequired(column, meta.columns) || required" class="text-red-500">&nbsp;*</span>
<template v-if="!hideMenu">
<div class="flex-1" />

85
packages/nc-gui/composables/useExpandedFormStore.ts

@ -9,11 +9,13 @@ import {
extractSdkResponseErrorMsg,
getHTMLEncodedText,
message,
populateInsertObject,
ref,
useApi,
useI18n,
useInjectionState,
useKanbanViewStoreOrThrow,
useMetas,
useNuxtApp,
useProject,
useProvideSmartsheetRowStore,
@ -132,53 +134,66 @@ const [useProvideExpandedFormStore, useExpandedFormStore] = useInjectionState((m
$e('a:row-expand:comment')
}
const save = async () => {
const save = async (ltarState: Record<string, any> = {}) => {
let data
try {
const updateOrInsertObj = [...changedColumns.value].reduce((obj, col) => {
obj[col] = row.value.row[col]
return obj
}, {} as Record<string, any>)
const isNewRow = row.value.rowMeta?.new ?? false
if (isNewRow) {
data = await $api.dbTableRow.create('noco', project.value.title as string, meta.value.title, updateOrInsertObj)
const { getMeta } = useMetas()
const { missingRequiredColumns, insertObj } = await populateInsertObject({
meta: meta.value,
ltarState,
getMeta,
row: row.value.row,
throwError: true,
})
if (missingRequiredColumns.size) return
data = await $api.dbTableRow.create('noco', project.value.title as string, meta.value.title, insertObj)
Object.assign(row.value, {
row: data,
rowMeta: {},
oldRow: { ...data },
})
} else if (Object.keys(updateOrInsertObj).length) {
const id = extractPkFromRow(row.value.row, meta.value.columns as ColumnType[])
if (!id) {
return message.info("Update not allowed for table which doesn't have primary Key")
}
await $api.dbTableRow.update(NOCO, project.value.title as string, meta.value.title, id, updateOrInsertObj)
for (const key of Object.keys(updateOrInsertObj)) {
// audit
$api.utils
.auditRowUpdate(id, {
fk_model_id: meta.value.id,
column_name: key,
row_id: id,
value: getHTMLEncodedText(updateOrInsertObj[key]),
prev_value: getHTMLEncodedText(row.value.oldRow[key]),
})
.then(async () => {
/** load latest comments/audit if right drawer is open */
if (commentsDrawer.value) {
await loadCommentsAndLogs()
}
})
}
} else {
// No columns to update
return message.info(t('msg.info.noColumnsToUpdate'))
const updateOrInsertObj = [...changedColumns.value].reduce((obj, col) => {
obj[col] = row.value.row[col]
return obj
}, {} as Record<string, any>)
if (Object.keys(updateOrInsertObj).length) {
const id = extractPkFromRow(row.value.row, meta.value.columns as ColumnType[])
if (!id) {
return message.info("Update not allowed for table which doesn't have primary Key")
}
await $api.dbTableRow.update(NOCO, project.value.title as string, meta.value.title, id, updateOrInsertObj)
for (const key of Object.keys(updateOrInsertObj)) {
// audit
$api.utils
.auditRowUpdate(id, {
fk_model_id: meta.value.id,
column_name: key,
row_id: id,
value: getHTMLEncodedText(updateOrInsertObj[key]),
prev_value: getHTMLEncodedText(row.value.oldRow[key]),
})
.then(async () => {
/** load latest comments/audit if right drawer is open */
if (commentsDrawer.value) {
await loadCommentsAndLogs()
}
})
}
} else {
// No columns to update
return message.info(t('msg.info.noColumnsToUpdate'))
}
}
if (activeView.value?.type === ViewTypes.KANBAN) {

18
packages/nc-gui/composables/useSmartsheetRowStore.ts

@ -65,12 +65,18 @@ const [useProvideSmartsheetRowStore, useSmartsheetRowStore] = useInjectionState(
}
}
const linkRecord = async (rowId: string, relatedRowId: string, column: ColumnType, type: RelationTypes) => {
const linkRecord = async (
rowId: string,
relatedRowId: string,
column: ColumnType,
type: RelationTypes,
{ metaValue = meta.value }: { metaValue?: TableType } = {},
) => {
try {
await $api.dbTableRow.nestedAdd(
NOCO,
project.value.title as string,
meta.value?.title as string,
metaValue?.title as string,
rowId,
type as 'mm' | 'hm',
column.title as string,
@ -82,9 +88,9 @@ const [useProvideSmartsheetRowStore, useSmartsheetRowStore] = useInjectionState(
}
/** sync LTAR relations kept in local state */
const syncLTARRefs = async (row: Record<string, any>) => {
const id = extractPkFromRow(row, meta.value?.columns as ColumnType[])
for (const column of meta?.value?.columns ?? []) {
const syncLTARRefs = async (row: Record<string, any>, { metaValue = meta.value }: { metaValue?: TableType } = {}) => {
const id = extractPkFromRow(row, metaValue?.columns as ColumnType[])
for (const column of metaValue?.columns ?? []) {
if (column.uidt !== UITypes.LinkToAnotherRecord) continue
const colOptions = column?.colOptions as LinkToAnotherRecordType
@ -99,6 +105,7 @@ const [useProvideSmartsheetRowStore, useSmartsheetRowStore] = useInjectionState(
extractPkFromRow(relatedRow, relatedTableMeta.columns as ColumnType[]),
column,
colOptions.type as RelationTypes,
{ metaValue },
)
}
} else if (isBt && state?.value?.[column.title!]) {
@ -107,6 +114,7 @@ const [useProvideSmartsheetRowStore, useSmartsheetRowStore] = useInjectionState(
extractPkFromRow(state.value?.[column.title!] as Record<string, any>, relatedTableMeta.columns as ColumnType[]),
column,
colOptions.type as RelationTypes,
{ metaValue },
)
}
}

50
packages/nc-gui/composables/useViewData.ts

@ -9,10 +9,12 @@ import {
extractSdkResponseErrorMsg,
getHTMLEncodedText,
message,
populateInsertObject,
ref,
useApi,
useGlobal,
useI18n,
useMetas,
useNuxtApp,
useProject,
useSharedView,
@ -194,20 +196,29 @@ export function useViewData(
galleryData.value = await $api.dbView.galleryRead(viewMeta.value.id)
}
async function insertRow(row: Record<string, any>, rowIndex = formattedData.value?.length) {
async function insertRow(
row: Record<string, any>,
rowIndex = formattedData.value?.length,
ltarState: Record<string, any> = {},
{ metaValue = meta.value, viewMetaValue = viewMeta.value }: { metaValue?: TableType; viewMetaValue?: ViewType } = {},
) {
try {
const insertObj = meta?.value?.columns?.reduce((o: any, col) => {
if (!col.ai && row?.[col.title as string] !== null) {
o[col.title as string] = row?.[col.title as string]
}
return o
}, {})
const { getMeta } = useMetas()
const { missingRequiredColumns, insertObj } = await populateInsertObject({
meta: metaValue!,
ltarState,
getMeta,
row,
})
if (missingRequiredColumns.size) return
const insertedData = await $api.dbViewRow.create(
NOCO,
project?.value.id as string,
meta.value?.id as string,
viewMeta?.value?.id as string,
metaValue?.id as string,
viewMetaValue?.id as string,
insertObj,
)
@ -224,15 +235,19 @@ export function useViewData(
}
}
async function updateRowProperty(toUpdate: Row, property: string) {
async function updateRowProperty(
toUpdate: Row,
property: string,
{ metaValue = meta.value, viewMetaValue = viewMeta.value }: { metaValue?: TableType; viewMetaValue?: ViewType } = {},
) {
try {
const id = extractPkFromRow(toUpdate.row, meta.value?.columns as ColumnType[])
const updatedRowData = await $api.dbViewRow.update(
NOCO,
project?.value.id as string,
meta.value?.id as string,
viewMeta?.value?.id as string,
metaValue?.id as string,
viewMetaValue?.id as string,
id,
{
[property]: toUpdate.row[property],
@ -261,11 +276,16 @@ export function useViewData(
}
}
async function updateOrSaveRow(row: Row, property?: string) {
async function updateOrSaveRow(
row: Row,
property?: string,
ltarState?: Record<string, any>,
args: { metaValue?: TableType; viewMetaValue?: ViewType } = {},
) {
if (row.rowMeta.new) {
return await insertRow(row.row, formattedData.value.indexOf(row))
return await insertRow(row.row, formattedData.value.indexOf(row), ltarState, args)
} else {
await updateRowProperty(row, property!)
await updateRowProperty(row, property!, args)
}
}

15
packages/nc-gui/utils/columnUtils.ts

@ -1,4 +1,5 @@
import { UITypes } from 'nocodb-sdk'
import type { ColumnType, LinkToAnotherRecordType } from 'nocodb-sdk'
import { RelationTypes, UITypes } from 'nocodb-sdk'
import LinkVariant from '~icons/mdi/link-variant'
import TableColumnPlusBefore from '~icons/mdi/table-column-plus-before'
import FormatColorText from '~icons/mdi/format-color-text'
@ -167,7 +168,17 @@ const getUIDTIcon = (uidt: UITypes | string) => {
).icon
}
export { uiTypes, getUIDTIcon }
const isColumnRequired = (col?: ColumnType) => col && col.rqd && !col.cdf && !col.ai
const isVirtualColRequired = (col: ColumnType, columns: ColumnType[]) =>
col.uidt === UITypes.LinkToAnotherRecord &&
(<LinkToAnotherRecordType>col.colOptions).type === RelationTypes.BELONGS_TO &&
isColumnRequired(columns.find((c) => c.id === (<LinkToAnotherRecordType>col.colOptions).fk_child_column_id))
const isColumnRequiredAndNull = (col: ColumnType, row: Record<string, any>) => {
return isColumnRequired(col) && (row[col.title!] === undefined || row[col.title!] === null)
}
export { uiTypes, getUIDTIcon, isColumnRequiredAndNull, isColumnRequired, isVirtualColRequired }
/**
* @copyright Copyright (c) 2021, Xgene Cloud Ltd

57
packages/nc-gui/utils/dataUtils.ts

@ -1,4 +1,5 @@
import type { ColumnType } from 'nocodb-sdk'
import { RelationTypes, UITypes } from 'nocodb-sdk'
import type { ColumnType, LinkToAnotherRecordType, TableInfoType, TableType } from 'nocodb-sdk'
export const extractPkFromRow = (row: Record<string, any>, columns: ColumnType[]) => {
return (
@ -9,3 +10,57 @@ export const extractPkFromRow = (row: Record<string, any>, columns: ColumnType[]
.join('___')
)
}
// a function to populate insert object and verify if all required fields are present
export async function populateInsertObject({
getMeta,
row,
meta,
ltarState,
throwError,
}: {
meta: TableType
ltarState: Record<string, any>
getMeta: (tableIdOrTitle: string, force?: boolean) => Promise<TableType | TableInfoType | null>
row: Record<string, any>
throwError?: boolean
}) {
const missingRequiredColumns = new Set()
const insertObj = await meta.columns?.reduce(async (_o: Promise<any>, col) => {
const o = await _o
// if column is BT relation then check if foreign key is not_null(required)
if (
ltarState &&
col.uidt === UITypes.LinkToAnotherRecord &&
(<LinkToAnotherRecordType>col.colOptions).type === RelationTypes.BELONGS_TO
) {
if (ltarState[col.title!]) {
const colOpt = <LinkToAnotherRecordType>col.colOptions
const childCol = meta.columns!.find((c) => colOpt.fk_child_column_id === c.id)
const relatedTableMeta = (await getMeta(colOpt.fk_related_model_id!)) as TableType
if (relatedTableMeta && childCol) {
o[childCol.title!] =
ltarState[col.title!][relatedTableMeta!.columns!.find((c) => c.id === colOpt.fk_parent_column_id)!.title!]
missingRequiredColumns.delete(childCol.title)
}
}
}
// check all the required columns are not null
if (isColumnRequiredAndNull(col, row)) {
missingRequiredColumns.add(col.title)
}
if (!col.ai && row?.[col.title as string] !== null) {
o[col.title as string] = row?.[col.title as string]
}
return o
}, Promise.resolve({}))
if (throwError && missingRequiredColumns.size) {
throw new Error(`Missing required columns: ${[...missingRequiredColumns].join(', ')}`)
}
return { missingRequiredColumns, insertObj }
}

281
packages/nocodb-sdk/package-lock.json generated

@ -538,6 +538,19 @@
"node": ">=8"
}
},
"node_modules/nyc/node_modules/yargs-parser": {
"version": "18.1.3",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"dev": true,
"dependencies": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/table/node_modules/ansi-styles": {
"version": "4.3.0",
"dev": true,
@ -1565,6 +1578,18 @@
"node": ">=10"
}
},
"node_modules/gh-pages/node_modules/array-union": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
"integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==",
"dev": true,
"dependencies": {
"array-uniq": "^1.0.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/escape-goat": {
"version": "2.1.1",
"dev": true,
@ -1786,6 +1811,15 @@
"node": ">=0.10.0"
}
},
"node_modules/nyc/node_modules/camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"dev": true,
"engines": {
"node": ">=6"
}
},
"node_modules/npm-run-all/node_modules/path-key": {
"version": "2.0.1",
"dev": true,
@ -2106,6 +2140,15 @@
"node": ">= 8"
}
},
"node_modules/gh-pages/node_modules/universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
"dev": true,
"engines": {
"node": ">= 4.0.0"
}
},
"node_modules/signal-exit": {
"version": "3.0.7",
"dev": true,
@ -2539,6 +2582,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/typedoc/node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/trim-repeated": {
"version": "1.0.0",
"dev": true,
@ -2970,6 +3022,18 @@
"node": ">=8"
}
},
"node_modules/git-semver-tags/node_modules/type-fest": {
"version": "0.18.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz",
"integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==",
"dev": true,
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/strip-indent": {
"version": "3.0.0",
"dev": true,
@ -4094,6 +4158,15 @@
"node": ">=0.8.0"
}
},
"node_modules/inquirer/node_modules/is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
"dev": true,
"engines": {
"node": ">=4"
}
},
"node_modules/dotgitignore/node_modules/path-exists": {
"version": "3.0.0",
"dev": true,
@ -5144,6 +5217,15 @@
"once": "^1.4.0"
}
},
"node_modules/array-uniq": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
"integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/escalade": {
"version": "3.1.1",
"dev": true,
@ -5160,6 +5242,21 @@
"node": ">=4.0.0"
}
},
"node_modules/standard-version/node_modules/p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
"dev": true,
"dependencies": {
"yocto-queue": "^0.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/eslint-module-utils/node_modules/p-limit": {
"version": "1.3.0",
"dev": true,
@ -5596,6 +5693,15 @@
"node": "^10.12.0 || >=12.0.0"
}
},
"node_modules/standard-version/node_modules/escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"dev": true,
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/js-tokens": {
"version": "4.0.0",
"dev": true,
@ -7101,6 +7207,18 @@
"node": ">=4.8"
}
},
"node_modules/standard-version/node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"dependencies": {
"has-flag": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/spawn-wrap": {
"version": "2.0.0",
"dev": true,
@ -7133,6 +7251,18 @@
"dev": true,
"license": "MIT"
},
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
"dev": true,
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/strip-json-comments": {
"version": "2.0.1",
"dev": true,
@ -7210,6 +7340,21 @@
"license": "MIT",
"optional": true
},
"node_modules/standard-version/node_modules/p-locate": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
"dev": true,
"dependencies": {
"p-limit": "^3.0.2"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@nodelib/fs.walk": {
"version": "1.2.8",
"dev": true,
@ -7463,6 +7608,21 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/standard-version/node_modules/locate-path": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
"dev": true,
"dependencies": {
"p-locate": "^5.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/detect-newline": {
"version": "3.1.0",
"dev": true,
@ -8451,6 +8611,15 @@
"ini": "^1.3.2"
}
},
"node_modules/standard-version/node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"dev": true,
"engines": {
"node": ">=4"
}
},
"node_modules/semver-diff": {
"version": "3.1.1",
"dev": true,
@ -10468,6 +10637,12 @@
"version": "2.1.0",
"dev": true
},
"array-uniq": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
"integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==",
"dev": true
},
"array.prototype.flat": {
"version": "1.2.5",
"dev": true,
@ -12614,6 +12789,15 @@
"globby": "^6.1.0"
},
"dependencies": {
"array-union": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
"integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==",
"dev": true,
"requires": {
"array-uniq": "^1.0.1"
}
},
"commander": {
"version": "2.20.3",
"dev": true
@ -12641,6 +12825,12 @@
"pify": {
"version": "2.3.0",
"dev": true
},
"universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
"dev": true
}
}
},
@ -12737,6 +12927,12 @@
"semver": {
"version": "6.3.0",
"dev": true
},
"type-fest": {
"version": "0.18.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz",
"integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==",
"dev": true
}
}
},
@ -13104,6 +13300,12 @@
"version": "3.0.0",
"dev": true
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
"dev": true
},
"restore-cursor": {
"version": "2.0.0",
"dev": true,
@ -14064,6 +14266,12 @@
"color-convert": "^2.0.1"
}
},
"camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"dev": true
},
"cliui": {
"version": "6.0.0",
"dev": true,
@ -14105,6 +14313,16 @@
"y18n": "^4.0.0",
"yargs-parser": "^18.1.2"
}
},
"yargs-parser": {
"version": "18.1.3",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"dev": true,
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
}
}
},
@ -15042,6 +15260,12 @@
"supports-color": "^5.3.0"
}
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"dev": true
},
"find-up": {
"version": "5.0.0",
"dev": true,
@ -15049,6 +15273,48 @@
"locate-path": "^6.0.0",
"path-exists": "^4.0.0"
}
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"dev": true
},
"locate-path": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
"dev": true,
"requires": {
"p-locate": "^5.0.0"
}
},
"p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
"dev": true,
"requires": {
"yocto-queue": "^0.1.0"
}
},
"p-locate": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
"dev": true,
"requires": {
"p-limit": "^3.0.2"
}
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
}
}
},
@ -15528,6 +15794,15 @@
"shiki": "^0.10.1"
},
"dependencies": {
"brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
"requires": {
"balanced-match": "^1.0.0"
}
},
"glob": {
"version": "8.0.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz",
@ -15804,6 +16079,12 @@
"yn": {
"version": "3.1.1",
"dev": true
},
"yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
"dev": true
}
}
}

2
packages/nocodb/tests/pg-sakila-db/01-postgres-sakila-schema.sql

@ -168,7 +168,7 @@ CREATE TABLE film (
rating mpaa_rating DEFAULT 'G'::mpaa_rating,
last_update timestamp without time zone DEFAULT now() NOT NULL,
special_features text[],
fulltext tsvector NOT NULL
fulltext tsvector
);

Loading…
Cancel
Save