422 lines
12 KiB
JavaScript
422 lines
12 KiB
JavaScript
import { entityKind, is } from "../entity.js";
|
|
import { isPgEnum } from "../pg-core/columns/enum.js";
|
|
import { Subquery } from "../subquery.js";
|
|
import { tracer } from "../tracing.js";
|
|
import { ViewBaseConfig } from "../view-common.js";
|
|
import { Column } from "../column.js";
|
|
import { IsAlias, Table } from "../table.js";
|
|
class FakePrimitiveParam {
|
|
static [entityKind] = "FakePrimitiveParam";
|
|
}
|
|
function isSQLWrapper(value) {
|
|
return value !== null && value !== void 0 && typeof value.getSQL === "function";
|
|
}
|
|
function mergeQueries(queries) {
|
|
const result = { sql: "", params: [] };
|
|
for (const query of queries) {
|
|
result.sql += query.sql;
|
|
result.params.push(...query.params);
|
|
if (query.typings?.length) {
|
|
if (!result.typings) {
|
|
result.typings = [];
|
|
}
|
|
result.typings.push(...query.typings);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
class StringChunk {
|
|
static [entityKind] = "StringChunk";
|
|
value;
|
|
constructor(value) {
|
|
this.value = Array.isArray(value) ? value : [value];
|
|
}
|
|
getSQL() {
|
|
return new SQL([this]);
|
|
}
|
|
}
|
|
class SQL {
|
|
constructor(queryChunks) {
|
|
this.queryChunks = queryChunks;
|
|
}
|
|
static [entityKind] = "SQL";
|
|
/** @internal */
|
|
decoder = noopDecoder;
|
|
shouldInlineParams = false;
|
|
append(query) {
|
|
this.queryChunks.push(...query.queryChunks);
|
|
return this;
|
|
}
|
|
toQuery(config) {
|
|
return tracer.startActiveSpan("drizzle.buildSQL", (span) => {
|
|
const query = this.buildQueryFromSourceParams(this.queryChunks, config);
|
|
span?.setAttributes({
|
|
"drizzle.query.text": query.sql,
|
|
"drizzle.query.params": JSON.stringify(query.params)
|
|
});
|
|
return query;
|
|
});
|
|
}
|
|
buildQueryFromSourceParams(chunks, _config) {
|
|
const config = Object.assign({}, _config, {
|
|
inlineParams: _config.inlineParams || this.shouldInlineParams,
|
|
paramStartIndex: _config.paramStartIndex || { value: 0 }
|
|
});
|
|
const {
|
|
casing,
|
|
escapeName,
|
|
escapeParam,
|
|
prepareTyping,
|
|
inlineParams,
|
|
paramStartIndex
|
|
} = config;
|
|
return mergeQueries(chunks.map((chunk) => {
|
|
if (is(chunk, StringChunk)) {
|
|
return { sql: chunk.value.join(""), params: [] };
|
|
}
|
|
if (is(chunk, Name)) {
|
|
return { sql: escapeName(chunk.value), params: [] };
|
|
}
|
|
if (chunk === void 0) {
|
|
return { sql: "", params: [] };
|
|
}
|
|
if (Array.isArray(chunk)) {
|
|
const result = [new StringChunk("(")];
|
|
for (const [i, p] of chunk.entries()) {
|
|
result.push(p);
|
|
if (i < chunk.length - 1) {
|
|
result.push(new StringChunk(", "));
|
|
}
|
|
}
|
|
result.push(new StringChunk(")"));
|
|
return this.buildQueryFromSourceParams(result, config);
|
|
}
|
|
if (is(chunk, SQL)) {
|
|
return this.buildQueryFromSourceParams(chunk.queryChunks, {
|
|
...config,
|
|
inlineParams: inlineParams || chunk.shouldInlineParams
|
|
});
|
|
}
|
|
if (is(chunk, Table)) {
|
|
const schemaName = chunk[Table.Symbol.Schema];
|
|
const tableName = chunk[Table.Symbol.Name];
|
|
return {
|
|
sql: schemaName === void 0 ? escapeName(tableName) : escapeName(schemaName) + "." + escapeName(tableName),
|
|
params: []
|
|
};
|
|
}
|
|
if (is(chunk, Column)) {
|
|
const columnName = casing.getColumnCasing(chunk);
|
|
if (_config.invokeSource === "indexes") {
|
|
return { sql: escapeName(columnName), params: [] };
|
|
}
|
|
const schemaName = chunk.table[Table.Symbol.Schema];
|
|
return {
|
|
sql: chunk.table[IsAlias] || schemaName === void 0 ? escapeName(chunk.table[Table.Symbol.Name]) + "." + escapeName(columnName) : escapeName(schemaName) + "." + escapeName(chunk.table[Table.Symbol.Name]) + "." + escapeName(columnName),
|
|
params: []
|
|
};
|
|
}
|
|
if (is(chunk, View)) {
|
|
const schemaName = chunk[ViewBaseConfig].schema;
|
|
const viewName = chunk[ViewBaseConfig].name;
|
|
return {
|
|
sql: schemaName === void 0 ? escapeName(viewName) : escapeName(schemaName) + "." + escapeName(viewName),
|
|
params: []
|
|
};
|
|
}
|
|
if (is(chunk, Param)) {
|
|
if (is(chunk.value, Placeholder)) {
|
|
return { sql: escapeParam(paramStartIndex.value++, chunk), params: [chunk], typings: ["none"] };
|
|
}
|
|
const mappedValue = chunk.value === null ? null : chunk.encoder.mapToDriverValue(chunk.value);
|
|
if (is(mappedValue, SQL)) {
|
|
return this.buildQueryFromSourceParams([mappedValue], config);
|
|
}
|
|
if (inlineParams) {
|
|
return { sql: this.mapInlineParam(mappedValue, config), params: [] };
|
|
}
|
|
let typings = ["none"];
|
|
if (prepareTyping) {
|
|
typings = [prepareTyping(chunk.encoder)];
|
|
}
|
|
return { sql: escapeParam(paramStartIndex.value++, mappedValue), params: [mappedValue], typings };
|
|
}
|
|
if (is(chunk, Placeholder)) {
|
|
return { sql: escapeParam(paramStartIndex.value++, chunk), params: [chunk], typings: ["none"] };
|
|
}
|
|
if (is(chunk, SQL.Aliased) && chunk.fieldAlias !== void 0) {
|
|
return { sql: escapeName(chunk.fieldAlias), params: [] };
|
|
}
|
|
if (is(chunk, Subquery)) {
|
|
if (chunk._.isWith) {
|
|
return { sql: escapeName(chunk._.alias), params: [] };
|
|
}
|
|
return this.buildQueryFromSourceParams([
|
|
new StringChunk("("),
|
|
chunk._.sql,
|
|
new StringChunk(") "),
|
|
new Name(chunk._.alias)
|
|
], config);
|
|
}
|
|
if (isPgEnum(chunk)) {
|
|
if (chunk.schema) {
|
|
return { sql: escapeName(chunk.schema) + "." + escapeName(chunk.enumName), params: [] };
|
|
}
|
|
return { sql: escapeName(chunk.enumName), params: [] };
|
|
}
|
|
if (isSQLWrapper(chunk)) {
|
|
if (chunk.shouldOmitSQLParens?.()) {
|
|
return this.buildQueryFromSourceParams([chunk.getSQL()], config);
|
|
}
|
|
return this.buildQueryFromSourceParams([
|
|
new StringChunk("("),
|
|
chunk.getSQL(),
|
|
new StringChunk(")")
|
|
], config);
|
|
}
|
|
if (inlineParams) {
|
|
return { sql: this.mapInlineParam(chunk, config), params: [] };
|
|
}
|
|
return { sql: escapeParam(paramStartIndex.value++, chunk), params: [chunk], typings: ["none"] };
|
|
}));
|
|
}
|
|
mapInlineParam(chunk, { escapeString }) {
|
|
if (chunk === null) {
|
|
return "null";
|
|
}
|
|
if (typeof chunk === "number" || typeof chunk === "boolean") {
|
|
return chunk.toString();
|
|
}
|
|
if (typeof chunk === "string") {
|
|
return escapeString(chunk);
|
|
}
|
|
if (typeof chunk === "object") {
|
|
const mappedValueAsString = chunk.toString();
|
|
if (mappedValueAsString === "[object Object]") {
|
|
return escapeString(JSON.stringify(chunk));
|
|
}
|
|
return escapeString(mappedValueAsString);
|
|
}
|
|
throw new Error("Unexpected param value: " + chunk);
|
|
}
|
|
getSQL() {
|
|
return this;
|
|
}
|
|
as(alias) {
|
|
if (alias === void 0) {
|
|
return this;
|
|
}
|
|
return new SQL.Aliased(this, alias);
|
|
}
|
|
mapWith(decoder) {
|
|
this.decoder = typeof decoder === "function" ? { mapFromDriverValue: decoder } : decoder;
|
|
return this;
|
|
}
|
|
inlineParams() {
|
|
this.shouldInlineParams = true;
|
|
return this;
|
|
}
|
|
/**
|
|
* This method is used to conditionally include a part of the query.
|
|
*
|
|
* @param condition - Condition to check
|
|
* @returns itself if the condition is `true`, otherwise `undefined`
|
|
*/
|
|
if(condition) {
|
|
return condition ? this : void 0;
|
|
}
|
|
}
|
|
class Name {
|
|
constructor(value) {
|
|
this.value = value;
|
|
}
|
|
static [entityKind] = "Name";
|
|
brand;
|
|
getSQL() {
|
|
return new SQL([this]);
|
|
}
|
|
}
|
|
function name(value) {
|
|
return new Name(value);
|
|
}
|
|
function isDriverValueEncoder(value) {
|
|
return typeof value === "object" && value !== null && "mapToDriverValue" in value && typeof value.mapToDriverValue === "function";
|
|
}
|
|
const noopDecoder = {
|
|
mapFromDriverValue: (value) => value
|
|
};
|
|
const noopEncoder = {
|
|
mapToDriverValue: (value) => value
|
|
};
|
|
const noopMapper = {
|
|
...noopDecoder,
|
|
...noopEncoder
|
|
};
|
|
class Param {
|
|
/**
|
|
* @param value - Parameter value
|
|
* @param encoder - Encoder to convert the value to a driver parameter
|
|
*/
|
|
constructor(value, encoder = noopEncoder) {
|
|
this.value = value;
|
|
this.encoder = encoder;
|
|
}
|
|
static [entityKind] = "Param";
|
|
brand;
|
|
getSQL() {
|
|
return new SQL([this]);
|
|
}
|
|
}
|
|
function param(value, encoder) {
|
|
return new Param(value, encoder);
|
|
}
|
|
function sql(strings, ...params) {
|
|
const queryChunks = [];
|
|
if (params.length > 0 || strings.length > 0 && strings[0] !== "") {
|
|
queryChunks.push(new StringChunk(strings[0]));
|
|
}
|
|
for (const [paramIndex, param2] of params.entries()) {
|
|
queryChunks.push(param2, new StringChunk(strings[paramIndex + 1]));
|
|
}
|
|
return new SQL(queryChunks);
|
|
}
|
|
((sql2) => {
|
|
function empty() {
|
|
return new SQL([]);
|
|
}
|
|
sql2.empty = empty;
|
|
function fromList(list) {
|
|
return new SQL(list);
|
|
}
|
|
sql2.fromList = fromList;
|
|
function raw(str) {
|
|
return new SQL([new StringChunk(str)]);
|
|
}
|
|
sql2.raw = raw;
|
|
function join(chunks, separator) {
|
|
const result = [];
|
|
for (const [i, chunk] of chunks.entries()) {
|
|
if (i > 0 && separator !== void 0) {
|
|
result.push(separator);
|
|
}
|
|
result.push(chunk);
|
|
}
|
|
return new SQL(result);
|
|
}
|
|
sql2.join = join;
|
|
function identifier(value) {
|
|
return new Name(value);
|
|
}
|
|
sql2.identifier = identifier;
|
|
function placeholder2(name2) {
|
|
return new Placeholder(name2);
|
|
}
|
|
sql2.placeholder = placeholder2;
|
|
function param2(value, encoder) {
|
|
return new Param(value, encoder);
|
|
}
|
|
sql2.param = param2;
|
|
})(sql || (sql = {}));
|
|
((SQL2) => {
|
|
class Aliased {
|
|
constructor(sql2, fieldAlias) {
|
|
this.sql = sql2;
|
|
this.fieldAlias = fieldAlias;
|
|
}
|
|
static [entityKind] = "SQL.Aliased";
|
|
/** @internal */
|
|
isSelectionField = false;
|
|
getSQL() {
|
|
return this.sql;
|
|
}
|
|
/** @internal */
|
|
clone() {
|
|
return new Aliased(this.sql, this.fieldAlias);
|
|
}
|
|
}
|
|
SQL2.Aliased = Aliased;
|
|
})(SQL || (SQL = {}));
|
|
class Placeholder {
|
|
constructor(name2) {
|
|
this.name = name2;
|
|
}
|
|
static [entityKind] = "Placeholder";
|
|
getSQL() {
|
|
return new SQL([this]);
|
|
}
|
|
}
|
|
function placeholder(name2) {
|
|
return new Placeholder(name2);
|
|
}
|
|
function fillPlaceholders(params, values) {
|
|
return params.map((p) => {
|
|
if (is(p, Placeholder)) {
|
|
if (!(p.name in values)) {
|
|
throw new Error(`No value for placeholder "${p.name}" was provided`);
|
|
}
|
|
return values[p.name];
|
|
}
|
|
if (is(p, Param) && is(p.value, Placeholder)) {
|
|
if (!(p.value.name in values)) {
|
|
throw new Error(`No value for placeholder "${p.value.name}" was provided`);
|
|
}
|
|
return p.encoder.mapToDriverValue(values[p.value.name]);
|
|
}
|
|
return p;
|
|
});
|
|
}
|
|
const IsDrizzleView = Symbol.for("drizzle:IsDrizzleView");
|
|
class View {
|
|
static [entityKind] = "View";
|
|
/** @internal */
|
|
[ViewBaseConfig];
|
|
/** @internal */
|
|
[IsDrizzleView] = true;
|
|
constructor({ name: name2, schema, selectedFields, query }) {
|
|
this[ViewBaseConfig] = {
|
|
name: name2,
|
|
originalName: name2,
|
|
schema,
|
|
selectedFields,
|
|
query,
|
|
isExisting: !query,
|
|
isAlias: false
|
|
};
|
|
}
|
|
getSQL() {
|
|
return new SQL([this]);
|
|
}
|
|
}
|
|
function isView(view) {
|
|
return typeof view === "object" && view !== null && IsDrizzleView in view;
|
|
}
|
|
Column.prototype.getSQL = function() {
|
|
return new SQL([this]);
|
|
};
|
|
Table.prototype.getSQL = function() {
|
|
return new SQL([this]);
|
|
};
|
|
Subquery.prototype.getSQL = function() {
|
|
return new SQL([this]);
|
|
};
|
|
export {
|
|
FakePrimitiveParam,
|
|
Name,
|
|
Param,
|
|
Placeholder,
|
|
SQL,
|
|
StringChunk,
|
|
View,
|
|
fillPlaceholders,
|
|
isDriverValueEncoder,
|
|
isSQLWrapper,
|
|
isView,
|
|
name,
|
|
noopDecoder,
|
|
noopEncoder,
|
|
noopMapper,
|
|
param,
|
|
placeholder,
|
|
sql
|
|
};
|
|
//# sourceMappingURL=sql.js.map
|