Skip to content
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

Kit bugfixes #4043

Open
wants to merge 21 commits into
base: beta
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions drizzle-kit/src/introspect-pg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,9 @@ export const relationsToTypeScriptForStudio = (
};

function generateIdentityParams(identity: Column['identity']) {
let paramsObj = `{ name: "${identity!.name}"`;
let paramsObj = `{ name: ${
identity!.name.startsWith('"') && identity!.name.endsWith('"') ? identity!.name : `"${identity!.name}"`
}`;
if (identity?.startWith) {
paramsObj += `, startWith: ${identity.startWith}`;
}
Expand Down Expand Up @@ -818,8 +820,9 @@ const mapDefault = (
return typeof defaultValue !== 'undefined' ? `.default(${mapColumnDefault(defaultValue, isExpression)})` : '';
}

if (lowered.startsWith('geometry')) {
return typeof defaultValue !== 'undefined' ? `.default(${mapColumnDefault(defaultValue, isExpression)})` : '';
if (lowered === 'geometry(point)') {
const match = defaultValue?.match(/\d+/g) ?? [];
return typeof defaultValue !== 'undefined' ? `.default([${match[0]}, ${match[1]}])` : '';
}

if (lowered.startsWith('vector')) {
Expand Down
15 changes: 15 additions & 0 deletions drizzle-kit/src/jsonStatements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,17 @@ export interface JsonAlterColumnTypeStatement {
columnAutoIncrement: boolean;
columnPk: boolean;
columnGenerated?: { as: string; type: 'stored' | 'virtual' };
identity?: {
type: 'added';
value: string;
} | {
type: 'deleted';
value: string;
} | {
type: 'changed';
old: string;
new: string;
} | undefined;
}

export interface JsonAlterColumnSetPrimaryKeyStatement {
Expand Down Expand Up @@ -558,6 +569,7 @@ export interface JsonAlterColumnSetIdentityStatement {
columnName: string;
schema: string;
identity: string;
changedSerialToIntegerIdentity: boolean;
}

export interface JsonAlterColumnDropIdentityStatement {
Expand Down Expand Up @@ -2100,6 +2112,7 @@ export const preparePgAlterColumns = (
columnNotNull,
columnAutoIncrement,
columnPk,
identity: column.identity,
});
}

Expand Down Expand Up @@ -2219,6 +2232,8 @@ export const preparePgAlterColumns = (
columnName,
schema,
identity: column.identity.value,
changedSerialToIntegerIdentity: column.type?.type === 'changed' && column.type.old === 'serial'
&& column.type.new === 'integer' && column.identity.type === 'added',
});
}

Expand Down
10 changes: 7 additions & 3 deletions drizzle-kit/src/serializer/mysqlSerializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
UniqueConstraint,
View,
} from '../serializer/mysqlSchema';
import { type DB, escapeSingleQuotes } from '../utils';
import { type DB, escapeSingleQuotes, replaceQueryParams } from '../utils';
import { getColumnCasing, sqlToStr } from './utils';

export const indexName = (tableName: string, columns: string[]) => {
Expand Down Expand Up @@ -167,6 +167,8 @@ export const generateMySqlSnapshot = (
.slice(0, 23)
}'`;
}
} else if (typeof column.default === 'bigint') {
columnToSet.default = column.default.toString();
} else {
columnToSet.default = column.default;
}
Expand Down Expand Up @@ -399,7 +401,7 @@ export const generateMySqlSnapshot = (

checkConstraintObject[checkName] = {
name: checkName,
value: dialect.sqlToQuery(check.value).sql,
value: replaceQueryParams('mysql', dialect.sqlToQuery(check.value)),
};
});

Expand Down Expand Up @@ -486,6 +488,8 @@ export const generateMySqlSnapshot = (
} else {
if (typeof column.default === 'string') {
columnToSet.default = `'${column.default}'`;
} else if (typeof column.default === 'bigint') {
columnToSet.default = column.default.toString();
} else {
if (sqlTypeLowered === 'json') {
columnToSet.default = `'${JSON.stringify(column.default)}'`;
Expand Down Expand Up @@ -973,7 +977,7 @@ AND
const tableName = checkConstraintRow['TABLE_NAME'];

const tableInResult = result[tableName];
// if (typeof tableInResult === 'undefined') continue;
if (typeof tableInResult === 'undefined') continue;

tableInResult.checkConstraint[constraintName] = {
name: constraintName,
Expand Down
39 changes: 33 additions & 6 deletions drizzle-kit/src/serializer/pgSerializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import type {
UniqueConstraint,
View,
} from '../serializer/pgSchema';
import { type DB, escapeSingleQuotes, isPgArrayType } from '../utils';
import { type DB, escapeSingleQuotes, isPgArrayType, replaceQueryParams } from '../utils';
import { getColumnCasing, sqlToStr } from './utils';

export const indexName = (tableName: string, columns: string[]) => {
Expand Down Expand Up @@ -239,6 +239,11 @@ export const generatePgSnapshot = (
if (column.default !== undefined) {
if (is(column.default, SQL)) {
columnToSet.default = sqlToStr(column.default, casing);
} else if (sqlTypeLowered === 'geometry(point)') {
const def = Array.isArray(column.default)
? column.default
: [(column.default as any).x, (column.default as any).y];
columnToSet.default = `st_geomfromtext('point(${def[0]}, ${def[1]})'::text)`;
} else {
if (typeof column.default === 'string') {
columnToSet.default = `'${escapeSingleQuotes(column.default)}'`;
Expand All @@ -255,6 +260,8 @@ export const generatePgSnapshot = (
}
} else if (isPgArrayType(sqlTypeLowered) && Array.isArray(column.default)) {
columnToSet.default = `'${buildArrayString(column.default, sqlTypeLowered)}'`;
} else if (typeof column.default === 'bigint') {
columnToSet.default = column.default.toString();
} else {
// Should do for all types
// columnToSet.default = `'${column.default}'::${sqlTypeLowered}`;
Expand Down Expand Up @@ -559,7 +566,7 @@ export const generatePgSnapshot = (

checksObject[checkName] = {
name: checkName,
value: dialect.sqlToQuery(check.value).sql,
value: replaceQueryParams('postgresql', dialect.sqlToQuery(check.value)),
};
});

Expand Down Expand Up @@ -819,11 +826,18 @@ export const generatePgSnapshot = (
if (column.default !== undefined) {
if (is(column.default, SQL)) {
columnToSet.default = sqlToStr(column.default, casing);
} else if (sqlTypeLowered === 'geometry(point)') {
const def = Array.isArray(column.default)
? column.default
: [(column.default as any).x, (column.default as any).y];
columnToSet.default = `st_geomfromtext('point(${def[0]}, ${def[1]})'::text)`;
} else {
if (typeof column.default === 'string') {
columnToSet.default = `'${column.default}'`;
} else if (typeof column.default === 'bigint') {
columnToSet.default = column.default.toString();
} else {
if (sqlTypeLowered === 'jsonb' || sqlTypeLowered === 'json') {
if (sqlTypeLowered === 'jsonb' || sqlTypeLowered === 'json' || sqlTypeLowered.startsWith('geometry')) {
columnToSet.default = `'${JSON.stringify(column.default)}'::${sqlTypeLowered}`;
} else if (column.default instanceof Date) {
if (sqlTypeLowered === 'date') {
Expand Down Expand Up @@ -1220,12 +1234,16 @@ WHERE
const tableResponse = await getColumnsInfoQuery({ schema: tableSchema, table: tableName, db });

const tableConstraints = await db.query(
`SELECT c.column_name, c.data_type, constraint_type, constraint_name, constraint_schema
`WITH constraints AS (
SELECT c.column_name, c.data_type, constraint_type, constraint_name, constraint_schema
FROM information_schema.table_constraints tc
JOIN information_schema.constraint_column_usage AS ccu USING (constraint_schema, constraint_name)
JOIN information_schema.columns AS c ON c.table_schema = tc.constraint_schema
AND tc.table_name = c.table_name AND ccu.column_name = c.column_name
WHERE tc.table_name = '${tableName}' and constraint_schema = '${tableSchema}';`,
WHERE tc.table_name = '${tableName}' and constraint_schema = '${tableSchema}'
) SELECT DISTINCT ON (c.column_name, c.constraint_name, c.constraint_schema) c.*, kcu.ordinal_position as position FROM constraints as c
LEFT JOIN information_schema.key_column_usage AS kcu USING (constraint_schema, constraint_name, column_name)
ORDER BY c.column_name, c.constraint_name, c.constraint_schema, position;`,
);

const tableChecks = await db.query(`SELECT
Expand Down Expand Up @@ -1387,7 +1405,10 @@ WHERE
const identityMaximum = columnResponse.identity_maximum;
const identityMinimum = columnResponse.identity_minimum;
const identityCycle = columnResponse.identity_cycle === 'YES';
const identityName = columnResponse.seq_name;
const identityName = columnResponse.seq_name && columnResponse.seq_name.startsWith('"')
&& columnResponse.seq_name.endsWith('"')
? columnResponse.seq_name.slice(1, -1)
: columnResponse.seq_name;

const primaryKey = tableConstraints.filter((mapRow) =>
columnName === mapRow.column_name && mapRow.constraint_type === 'PRIMARY KEY'
Expand Down Expand Up @@ -1478,6 +1499,9 @@ WHERE
.replace('character', 'char');

columnTypeMapped = trimChar(columnTypeMapped, '"');
columnTypeMapped = columnTypeMapped === 'geometry(Point)'
? columnTypeMapped.toLowerCase()
: columnTypeMapped;

columnToReturn[columnName] = {
name: columnName,
Expand Down Expand Up @@ -1780,6 +1804,9 @@ WHERE
.replace('character', 'char');

columnTypeMapped = trimChar(columnTypeMapped, '"');
columnTypeMapped = columnTypeMapped === 'geometry(Point)'
? columnTypeMapped.toLowerCase()
: columnTypeMapped;

columnToReturn[columnName] = {
name: columnName,
Expand Down
4 changes: 4 additions & 0 deletions drizzle-kit/src/serializer/singlestoreSerializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ export const generateSingleStoreSnapshot = (
} else {
if (typeof column.default === 'string') {
columnToSet.default = `'${column.default}'`;
} else if (typeof column.default === 'bigint') {
columnToSet.default = column.default.toString();
} else {
if (sqlTypeLowered === 'json' || Array.isArray(column.default)) {
columnToSet.default = `'${JSON.stringify(column.default)}'`;
Expand Down Expand Up @@ -359,6 +361,8 @@ export const generateSingleStoreSnapshot = (
} else {
if (typeof column.default === 'string') {
columnToSet.default = `'${column.default}'`;
} else if (typeof column.default === 'bigint') {
columnToSet.default = column.default.toString();
} else {
if (sqlTypeLowered === 'json') {
columnToSet.default = `'${JSON.stringify(column.default)}'`;
Expand Down
8 changes: 6 additions & 2 deletions drizzle-kit/src/serializer/sqliteSerializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import type {
UniqueConstraint,
View,
} from '../serializer/sqliteSchema';
import { escapeSingleQuotes, type SQLiteDB } from '../utils';
import { escapeSingleQuotes, replaceQueryParams, type SQLiteDB } from '../utils';
import { getColumnCasing, sqlToStr } from './utils';

export const generateSqliteSnapshot = (
Expand Down Expand Up @@ -91,6 +91,8 @@ export const generateSqliteSnapshot = (
} else {
columnToSet.default = typeof column.default === 'string'
? `'${escapeSingleQuotes(column.default)}'`
: typeof column.default === 'bigint'
? column.default.toString()
: typeof column.default === 'object'
|| Array.isArray(column.default)
? `'${JSON.stringify(column.default)}'`
Expand Down Expand Up @@ -309,7 +311,7 @@ export const generateSqliteSnapshot = (

checkConstraintObject[checkName] = {
name: checkName,
value: dialect.sqlToQuery(check.value).sql,
value: replaceQueryParams('sqlite', dialect.sqlToQuery(check.value)),
};
});

Expand Down Expand Up @@ -378,6 +380,8 @@ export const generateSqliteSnapshot = (
} else {
columnToSet.default = typeof column.default === 'string'
? `'${column.default}'`
: typeof column.default === 'bigint'
? column.default.toString()
: typeof column.default === 'object'
|| Array.isArray(column.default)
? `'${JSON.stringify(column.default)}'`
Expand Down
30 changes: 28 additions & 2 deletions drizzle-kit/src/sqlgenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ import { escapeSingleQuotes } from './utils';

const parseType = (schemaPrefix: string, type: string) => {
const pgNativeTypes = [
'bit',
'uuid',
'smallint',
'integer',
Expand Down Expand Up @@ -1006,7 +1007,7 @@ class PgAlterTableAlterColumnSetGenerated extends Convertor {
override convert(
statement: JsonAlterColumnSetIdentityStatement,
): string | string[] {
const { identity, tableName, columnName, schema } = statement;
const { identity, tableName, columnName, schema, changedSerialToIntegerIdentity } = statement;

const tableNameWithSchema = schema
? `"${schema}"."${tableName}"`
Expand Down Expand Up @@ -1042,6 +1043,17 @@ class PgAlterTableAlterColumnSetGenerated extends Convertor {
})`
: '';

const statementSql = `ALTER TABLE ${tableNameWithSchema} ALTER COLUMN "${columnName}" ADD${identityStatement};`;

if (changedSerialToIntegerIdentity) {
const getSerialStatement = `pg_get_serial_sequence('${schema ? `${schema}.` : ''}${tableName}', '${columnName}')`;
const coalesceStatement = `coalesce(max("${columnName}"), 0) + 1`;
return [
statementSql,
`SELECT setval(${getSerialStatement}, ${coalesceStatement}, false) FROM ${tableNameWithSchema};`,
];
}

return `ALTER TABLE ${tableNameWithSchema} ALTER COLUMN "${columnName}" ADD${identityStatement};`;
}
}
Expand Down Expand Up @@ -1877,12 +1889,26 @@ class PgAlterTableAlterColumnSetTypeConvertor extends Convertor {
}

convert(statement: JsonAlterColumnTypeStatement) {
const { tableName, columnName, newDataType, schema } = statement;
const { tableName, columnName, newDataType, schema, oldDataType, identity } = statement;

const tableNameWithSchema = schema
? `"${schema}"."${tableName}"`
: `"${tableName}"`;

// Switching from serial data type to using integer generated always as identity
if (oldDataType === 'serial' && newDataType === 'integer' && identity?.type === 'added') {
const unsquashedIdentity = PgSquasher.unsquashIdentity(identity.value);

const identityWithSchema = schema
? `"${schema}"."${unsquashedIdentity?.name}"`
: `"${unsquashedIdentity?.name}"`;

return [
`ALTER TABLE ${tableNameWithSchema} ALTER COLUMN "${columnName}" DROP DEFAULT;`,
`DROP SEQUENCE ${identityWithSchema};`,
];
}

return `ALTER TABLE ${tableNameWithSchema} ALTER COLUMN "${columnName}" SET DATA TYPE ${newDataType};`;
}
}
Expand Down
26 changes: 26 additions & 0 deletions drizzle-kit/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { RunResult } from 'better-sqlite3';
import chalk from 'chalk';
import type { Query } from 'drizzle-orm';
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'fs';
import { join } from 'path';
import { parse } from 'url';
Expand Down Expand Up @@ -107,6 +108,7 @@ export const prepareOutFolder = (out: string, dialect: Dialect) => {

const snapshots = readdirSync(meta)
.filter((it) => !it.startsWith('_'))
.filter((it) => it.endsWith('.json'))
.map((it) => join(meta, it));

snapshots.sort();
Expand Down Expand Up @@ -365,6 +367,30 @@ export function escapeSingleQuotes(str: string) {
}

export function unescapeSingleQuotes(str: string, ignoreFirstAndLastChar: boolean) {
if (str === "''") return str;
const regex = ignoreFirstAndLastChar ? /(?<!^)'(?!$)/g : /'/g;
return str.replace(/''/g, "'").replace(regex, "\\'");
}

export function replaceQueryParams(dialect: Dialect, query: Query): string {
let str = query.sql;
const params = query.params.map((p) => {
try {
return JSON.stringify(p);
} catch {
return String(p);
}
});

if (dialect === 'postgresql') {
for (let i = 0; i < params.length; i++) {
str = str.replace(`$${i + 1}`, params[i]);
}
return str;
}

for (const param of params) {
str = str.replace('?', param);
}
return str;
}
Loading