@sqg/sqg 0.19.1 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/index.mjs +329 -36
- package/dist/sqg.mjs +336 -37
- package/dist/templates/better-sqlite3.hbs +1 -1
- package/dist/templates/java-duckdb-arrow.hbs +3 -1
- package/dist/templates/java-jdbc.hbs +9 -0
- package/dist/templates/python.hbs +15 -9
- package/dist/templates/typescript-duckdb.hbs +8 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import consola, { LogLevels } from "consola";
|
|
|
3
3
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { LRParser } from "@lezer/lr";
|
|
5
5
|
import { homedir } from "node:os";
|
|
6
|
-
import { basename, dirname, extname, join, resolve } from "node:path";
|
|
6
|
+
import { basename, dirname, extname, join, relative, resolve } from "node:path";
|
|
7
7
|
import { camelCase, pascalCase, snakeCase } from "es-toolkit/string";
|
|
8
8
|
import Handlebars from "handlebars";
|
|
9
9
|
import YAML from "yaml";
|
|
@@ -229,6 +229,7 @@ SQL Annotation Syntax:
|
|
|
229
229
|
-- QUERY <name> [:one] [:pluck] Select query (returns rows)
|
|
230
230
|
-- EXEC <name> [:batch] Execute statement (INSERT/UPDATE/DELETE)
|
|
231
231
|
-- BASELINE <name> Schema created outside SQG (runs before migrations, not tracked)
|
|
232
|
+
-- BASELINE <name> :source=<src> Schema of a 'type: postgres' source (applied to a managed testcontainer, attached into DuckDB)
|
|
232
233
|
-- MIGRATE <name> Schema migration (runs in source order; name is any identifier, e.g. "1" or "add_email")
|
|
233
234
|
-- TESTDATA <name> Test data setup (not generated)
|
|
234
235
|
-- TABLE <name> :appender Table for bulk insert appender (DuckDB, PostgreSQL)
|
|
@@ -237,11 +238,19 @@ SQL Annotation Syntax:
|
|
|
237
238
|
\${varName} Reference a variable in SQL
|
|
238
239
|
|
|
239
240
|
Modifiers:
|
|
240
|
-
:one
|
|
241
|
-
:pluck
|
|
242
|
-
:all
|
|
243
|
-
:batch
|
|
244
|
-
:appender
|
|
241
|
+
:one Return single row (or null) instead of array
|
|
242
|
+
:pluck Return single column value (requires exactly 1 column)
|
|
243
|
+
:all Return all rows (default)
|
|
244
|
+
:batch Generate a JDBC batch method for an EXEC (Java only)
|
|
245
|
+
:appender Generate bulk insert appender for TABLE annotation
|
|
246
|
+
:source=Name Mark a BASELINE block as the schema of a postgres source (Name)
|
|
247
|
+
:result=Name Name (and share) the row type (Java only). Annotate ONE query
|
|
248
|
+
with :result=Name — every other query in the same file with the
|
|
249
|
+
same column shape automatically gets the same name; you do NOT
|
|
250
|
+
need to repeat the modifier on each query.
|
|
251
|
+
Without :result=, each query keeps its own per-query row type.
|
|
252
|
+
Exception: SELECT * matching a -- TABLE schema (same columns,
|
|
253
|
+
same order) auto-uses the table's row type with no annotation.
|
|
245
254
|
|
|
246
255
|
Example:
|
|
247
256
|
-- MIGRATE 1
|
|
@@ -405,7 +414,7 @@ const parser = LRParser.deserialize({
|
|
|
405
414
|
maxTerm: 34,
|
|
406
415
|
skippedNodes: [0],
|
|
407
416
|
repeatNodeCount: 6,
|
|
408
|
-
tokenData: "$KR~RqOX#YXY'wYZ(iZ]#Y]^$W^p#Ypq'wqr#Yrs(}st#Ytu6^uw#Ywx9[xz#Yz{%_{}#Y}!OKi!O!P#Y!P!Q#2P!Q![$A]![!]$Bu!]!_#Y!_!`$Eu!`!b#Y!b!c$Fu!c!}$A]!}#R#Y#R#S$A]#S#T#Y#T#o$A]#o;'S#Y;'S;=`'q<%lO#YU#_]_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{;'S#Y;'S;=`'q<%lO#YS$ZTOz$Wz{$j{;'S$W;'S;=`%X<%lO$WS$mVOz$Wz{$j{!P$W!P!Q%S!Q;'S$W;'S;=`%X<%lO$WS%XOnSS%[P;=`<%l$WU%d__QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{!P#Y!P!Q&c!Q;'S#Y;'S;=`'q<%lO#YU&jVnS_QOX'PZ]'P^p'Pqt'Pu;'S'P;'S;=`'k<%lO'PQ'UV_QOX'PZ]'P^p'Pqt'Pu;'S'P;'S;=`'k<%lO'PQ'nP;=`<%l'PU'tP;=`<%l#Y~'|Xj~OX$WXY'wYp$Wpq'wqz$Wz{$j{;'S$W;'S;=`%X<%lO$W~(nTk~Oz$Wz{$j{;'S$W;'S;=`%X<%lO$WU)Sb_QOX(}XY*[YZ$WZ](}]^*[^p(}pq*[qr(}rs.yst(}tu*[uz(}z{/y{#O(}#O#P5V#P;'S(};'S;=`6W<%lO(}U*_ZOY*[YZ$WZr*[rs+Qsz*[z{+f{#O*[#O#P.Z#P;'S*[;'S;=`.s<%lO*[U+VT]QOz$Wz{$j{;'S$W;'S;=`%X<%lO$WU+i]OY*[YZ$WZr*[rs+Qsz*[z{+f{!P*[!P!Q,b!Q#O*[#O#P.Z#P;'S*[;'S;=`.s<%lO*[U,gWnSOY-PZr-Prs-ls#O-P#O#P-q#P;'S-P;'S;=`.T<%lO-PQ-SWOY-PZr-Prs-ls#O-P#O#P-q#P;'S-P;'S;=`.T<%lO-PQ-qO]QQ-tTOY-PYZ-PZ;'S-P;'S;=`.T<%lO-PQ.WP;=`<%l-PU.^VOY*[YZ*[Zz*[z{+f{;'S*[;'S;=`.s<%lO*[U.vP;=`<%l*[U/Q]]Q_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{;'S#Y;'S;=`'q<%lO#YU0Od_QOX(}XY*[YZ$WZ](}]^*[^p(}pq*[qr(}rs.yst(}tu*[uz(}z{/y{!P(}!P!Q1^!Q#O(}#O#P5V#P;'S(};'S;=`6W<%lO(}U1e_nS_QOX2dXY-PZ]2d]^-P^p2dpq-Pqr2drs3hst2dtu-Pu#O2d#O#P4U#P;'S2d;'S;=`5P<%lO2dQ2i__QOX2dXY-PZ]2d]^-P^p2dpq-Pqr2drs3hst2dtu-Pu#O2d#O#P4U#P;'S2d;'S;=`5P<%lO2dQ3oV]Q_QOX'PZ]'P^p'Pqt'Pu;'S'P;'S;=`'k<%lO'PQ4Z[_QOX2dXY-PYZ-PZ]2d]^-P^p2dpq-Pqt2dtu-Pu;'S2d;'S;=`5P<%lO2dQ5SP;=`<%l2dU5[^_QOX(}XY*[YZ*[Z](}]^*[^p(}pq*[qt(}tu*[uz(}z{/y{;'S(};'S;=`6W<%lO(}U6ZP;=`<%l(}U6cV_QOz$Wz{$j{#o$W#o#p6x#p;'S$W;'S;=`%X<%lO$WU6{]Oz$Wz{$j{!Q$W!Q![7t![!c$W!c!}7t!}#R$W#R#S7t#S#T$W#T#o7t#o;'S$W;'S;=`%X<%lO$WU7w_Oz$Wz{$j{!Q$W!Q![7t![!c$W!c!}7t!}#R$W#R#S7t#S#T$W#T#o7t#o#q$W#q#r8v#r;'S$W;'S;=`%X<%lO$WU8{TaQOz$Wz{$j{;'S$W;'S;=`%X<%lO$WU9ab_QOX9[XY:iYZ$WZ]9[]^:i^p9[pq:iqt9[tu:iuw9[wxAVxz9[z{BV{#O9[#O#PHu#P;'S9[;'S;=`Kc<%lO9[U:lZOY:iYZ$WZw:iwx;_xz:iz{;s{#O:i#O#P?c#P;'S:i;'S;=`AP<%lO:iU;dT^QOz$Wz{$j{;'S$W;'S;=`%X<%lO$WU;v]OY:iYZ$WZw:iwx;_xz:iz{;s{!P:i!P!Q<o!Q#O:i#O#P?c#P;'S:i;'S;=`AP<%lO:iU<tWnSOY=^Zw=^wx=yx#O=^#O#P>O#P;'S=^;'S;=`?]<%lO=^Q=aWOY=^Zw=^wx=yx#O=^#O#P>O#P;'S=^;'S;=`?]<%lO=^Q>OO^QQ>RXOY=^YZ=^Zw=^wx>nx#O=^#O#P>O#P;'S=^;'S;=`?]<%lO=^Q>sW^QOY=^Zw=^wx=yx#O=^#O#P>O#P;'S=^;'S;=`?]<%lO=^Q?`P;=`<%l=^U?fZOY:iYZ:iZw:iwx@Xxz:iz{;s{#O:i#O#P?c#P;'S:i;'S;=`AP<%lO:iU@^Z^QOY:iYZ$WZw:iwx;_xz:iz{;s{#O:i#O#P?c#P;'S:i;'S;=`AP<%lO:iUASP;=`<%l:iUA^]^Q_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{;'S#Y;'S;=`'q<%lO#YUB[d_QOX9[XY:iYZ$WZ]9[]^:i^p9[pq:iqt9[tu:iuw9[wxAVxz9[z{BV{!P9[!P!QCj!Q#O9[#O#PHu#P;'S9[;'S;=`Kc<%lO9[UCq_nS_QOXDpXY=^Z]Dp]^=^^pDppq=^qtDptu=^uwDpwxEtx#ODp#O#PFb#P;'SDp;'S;=`Ho<%lODpQDu__QOXDpXY=^Z]Dp]^=^^pDppq=^qtDptu=^uwDpwxEtx#ODp#O#PFb#P;'SDp;'S;=`Ho<%lODpQE{V^Q_QOX'PZ]'P^p'Pqt'Pu;'S'P;'S;=`'k<%lO'PQFg`_QOXDpXY=^YZ=^Z]Dp]^=^^pDppq=^qtDptu=^uwDpwxGix#ODp#O#PFb#P;'SDp;'S;=`Ho<%lODpQGp_^Q_QOXDpXY=^Z]Dp]^=^^pDppq=^qtDptu=^uwDpwxEtx#ODp#O#PFb#P;'SDp;'S;=`Ho<%lODpQHrP;=`<%lDpUHzb_QOX9[XY:iYZ:iZ]9[]^:i^p9[pq:iqt9[tu:iuw9[wxJSxz9[z{BV{#O9[#O#PHu#P;'S9[;'S;=`Kc<%lO9[UJZb^Q_QOX9[XY:iYZ$WZ]9[]^:i^p9[pq:iqt9[tu:iuw9[wxAVxz9[z{BV{#O9[#O#PHu#P;'S9[;'S;=`Kc<%lO9[UKfP;=`<%l9[VKn__QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{}#Y}!OLm!O;'S#Y;'S;=`'q<%lO#YVLthQR_QOXN`XY!&|YZ$WZ]N`]^! c^pN`pq!&|qtN`tu! cuzN`z{!#q{!dN`!d!e!=`!e!gN`!g!h!Ft!h!oN`!o!p!Ja!p!sN`!s!t#!`!t!vN`!v!w#'U!w;'SN`;'S;=`!&v<%lON`VNg^QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{;'SN`;'S;=`!&v<%lON`V! hVQROY! cYZ$WZz! cz{! }{;'S! c;'S;=`!#k<%lO! cV!!SXQROY! cYZ$WZz! cz{! }{!P! c!P!Q!!o!Q;'S! c;'S;=`!#k<%lO! cV!!vSnSQROY!#SZ;'S!#S;'S;=`!#e<%lO!#SR!#XSQROY!#SZ;'S!#S;'S;=`!#e<%lO!#SR!#hP;=`<%l!#SV!#nP;=`<%l! cV!#x`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!PN`!P!Q!$z!Q;'SN`;'S;=`!&v<%lON`V!%TZnSQR_QOX!%vXY!#SZ]!%v]^!#S^p!%vpq!#Sqt!%vtu!#Su;'S!%v;'S;=`!&p<%lO!%vR!%}ZQR_QOX!%vXY!#SZ]!%v]^!#S^p!%vpq!#Sqt!%vtu!#Su;'S!%v;'S;=`!&p<%lO!%vR!&sP;=`<%l!%vV!&yP;=`<%lN`V!'RdQROX! cXY!&|YZ$WZp! cpq!&|qz! cz{! }{!d! c!d!e!(a!e!g! c!g!h!.l!h!o! c!o!p!0p!p!s! c!s!t!4W!t!v! c!v!w!6|!w;'S! c;'S;=`!#k<%lO! cV!(fXQROY! cYZ$WZz! cz{! }{!c! c!c!d!)R!d;'S! c;'S;=`!#k<%lO! cV!)WXQROY! cYZ$WZz! cz{! }{!u! c!u!v!)s!v;'S! c;'S;=`!#k<%lO! cV!)xXQROY! cYZ$WZz! cz{! }{!g! c!g!h!*e!h;'S! c;'S;=`!#k<%lO! cV!*jXQROY! cYZ$WZz! cz{! }{!n! c!n!o!+V!o;'S! c;'S;=`!#k<%lO! cV!+[XQROY! cYZ$WZz! cz{! }{!k! c!k!l!+w!l;'S! c;'S;=`!#k<%lO! cV!+|XQROY! cYZ$WZz! cz{! }{!p! c!p!q!,i!q;'S! c;'S;=`!#k<%lO! cV!,nXQROY! cYZ$WZz! cz{! }{!g! c!g!h!-Z!h;'S! c;'S;=`!#k<%lO! cV!-`YQROX! cXY!.OYZ$WZp! cpq!.Oqz! cz{! }{;'S! c;'S;=`!#k<%lO! cV!.VVYRQROY! cYZ$WZz! cz{! }{;'S! c;'S;=`!#k<%lO! cV!.qXQROY! cYZ$WZz! cz{! }{!z! c!z!{!/^!{;'S! c;'S;=`!#k<%lO! cV!/cXQROY! cYZ$WZz! cz{! }{!g! c!g!h!0O!h;'S! c;'S;=`!#k<%lO! cV!0TXQROY! cYZ$WZz! cz{! }{!e! c!e!f!-Z!f;'S! c;'S;=`!#k<%lO! cV!0uXQROY! cYZ$WZz! cz{! }{!k! c!k!l!1b!l;'S! c;'S;=`!#k<%lO! cV!1gXQROY! cYZ$WZz! cz{! }{!i! c!i!j!2S!j;'S! c;'S;=`!#k<%lO! cV!2XXQROY! cYZ$WZz! cz{! }{!t! c!t!u!2t!u;'S! c;'S;=`!#k<%lO! cV!2yXQROY! cYZ$WZz! cz{! }{!c! c!c!d!3f!d;'S! c;'S;=`!#k<%lO! cV!3kXQROY! cYZ$WZz! cz{! }{!v! c!v!w!,i!w;'S! c;'S;=`!#k<%lO! cV!4]XQROY! cYZ$WZz! cz{! }{!w! c!w!x!4x!x;'S! c;'S;=`!#k<%lO! cV!4}XQROY! cYZ$WZz! cz{! }{!g! c!g!h!5j!h;'S! c;'S;=`!#k<%lO! cV!5oXQROY! cYZ$WZz! cz{! }{!t! c!t!u!6[!u;'S! c;'S;=`!#k<%lO! cV!6aXQROY! cYZ$WZz! cz{! }{!{! c!{!|!-Z!|;'S! c;'S;=`!#k<%lO! cV!7RZQROY! cYZ$WZz! cz{! }{!c! c!c!d!7t!d!g! c!g!h!9W!h;'S! c;'S;=`!#k<%lO! cV!7yXQROY! cYZ$WZz! cz{! }{!d! c!d!e!8f!e;'S! c;'S;=`!#k<%lO! cV!8kXQROY! cYZ$WZz! cz{! }{!n! c!n!o!,i!o;'S! c;'S;=`!#k<%lO! cV!9]XQROY! cYZ$WZz! cz{! }{!u! c!u!v!9x!v;'S! c;'S;=`!#k<%lO! cV!9}XQROY! cYZ$WZz! cz{! }{!v! c!v!w!:j!w;'S! c;'S;=`!#k<%lO! cV!:oXQROY! cYZ$WZz! cz{! }{!f! c!f!g!;[!g;'S! c;'S;=`!#k<%lO! cV!;aXQROY! cYZ$WZz! cz{! }{!c! c!c!d!;|!d;'S! c;'S;=`!#k<%lO! cV!<RXQROY! cYZ$WZz! cz{! }{!v! c!v!w!<n!w;'S! c;'S;=`!#k<%lO! cV!<sXQROY! cYZ$WZz! cz{! }{!c! c!c!d!-Z!d;'S! c;'S;=`!#k<%lO! cV!=g`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!cN`!c!d!>i!d;'SN`;'S;=`!&v<%lON`V!>p`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!uN`!u!v!?r!v;'SN`;'S;=`!&v<%lON`V!?y`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!gN`!g!h!@{!h;'SN`;'S;=`!&v<%lON`V!AS`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!nN`!n!o!BU!o;'SN`;'S;=`!&v<%lON`V!B]`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!kN`!k!l!C_!l;'SN`;'S;=`!&v<%lON`V!Cf`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!pN`!p!q!Dh!q;'SN`;'S;=`!&v<%lON`V!Do`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!gN`!g!h!Eq!h;'SN`;'S;=`!&v<%lON`V!Ex^QR_QOXN`XY!.OYZ$WZ]N`]^! c^pN`pq!.OqtN`tu! cuzN`z{!#q{;'SN`;'S;=`!&v<%lON`V!F{`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!zN`!z!{!G}!{;'SN`;'S;=`!&v<%lON`V!HU`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!gN`!g!h!IW!h;'SN`;'S;=`!&v<%lON`V!I_`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!eN`!e!f!Eq!f;'SN`;'S;=`!&v<%lON`V!Jh`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!kN`!k!l!Kj!l;'SN`;'S;=`!&v<%lON`V!Kq`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!iN`!i!j!Ls!j;'SN`;'S;=`!&v<%lON`V!Lz`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!tN`!t!u!M|!u;'SN`;'S;=`!&v<%lON`V!NT`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!cN`!c!d# V!d;'SN`;'S;=`!&v<%lON`V# ^`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!vN`!v!w!Dh!w;'SN`;'S;=`!&v<%lON`V#!g`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!wN`!w!x##i!x;'SN`;'S;=`!&v<%lON`V##p`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!gN`!g!h#$r!h;'SN`;'S;=`!&v<%lON`V#$y`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!tN`!t!u#%{!u;'SN`;'S;=`!&v<%lON`V#&S`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!{N`!{!|!Eq!|;'SN`;'S;=`!&v<%lON`V#']bQR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!cN`!c!d#(e!d!gN`!g!h#*w!h;'SN`;'S;=`!&v<%lON`V#(l`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!dN`!d!e#)n!e;'SN`;'S;=`!&v<%lON`V#)u`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!nN`!n!o!Dh!o;'SN`;'S;=`!&v<%lON`V#+O`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!uN`!u!v#,Q!v;'SN`;'S;=`!&v<%lON`V#,X`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!vN`!v!w#-Z!w;'SN`;'S;=`!&v<%lON`V#-b`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!fN`!f!g#.d!g;'SN`;'S;=`!&v<%lON`V#.k`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!cN`!c!d#/m!d;'SN`;'S;=`!&v<%lON`V#/t`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!vN`!v!w#0v!w;'SN`;'S;=`!&v<%lON`V#0}`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!cN`!c!d!Eq!d;'SN`;'S;=`!&v<%lON`V#2U]_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{#2}{;'S#Y;'S;=`'q<%lO#YV#3Sj_QOX#4tXY#8uYZ#8uZ]#4t]^#5r^p#4tpq#8uqt#4ttu#5ruz#4tz{#6{{!P#4t!P!Q#Js!Q!d#4t!d!e$ p!e!g#4t!g!h$*_!h!o#4t!o!p$-k!p!s#4t!s!t$3P!t!v#4t!v!w$7a!w;'S#4t;'S;=`#8o<%lO#4tV#4y]_QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{;'S#4t;'S;=`#8o<%lO#4tV#5uTOz#5rz{#6U{;'S#5r;'S;=`#6u<%lO#5rV#6XVOz#5rz{#6U{!P#5r!P!Q#6n!Q;'S#5r;'S;=`#6u<%lO#5rV#6uORRnSV#6xP;=`<%l#5rV#7Q__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!P#4t!P!Q#8P!Q;'S#4t;'S;=`#8o<%lO#4tV#8YVRRnS_QOX'PZ]'P^p'Pqt'Pu;'S'P;'S;=`'k<%lO'PV#8rP;=`<%l#4tV#8xdOX#5rXY#8uYZ#8uZp#5rpq#8uqz#5rz{#6U{!d#5r!d!e#:W!e!g#5r!g!h#?o!h!o#5r!o!p#AZ!p!s#5r!s!t#Cw!t!v#5r!v!w#E{!w;'S#5r;'S;=`#6u<%lO#5rV#:ZVOz#5rz{#6U{!c#5r!c!d#:p!d;'S#5r;'S;=`#6u<%lO#5rV#:sVOz#5rz{#6U{!u#5r!u!v#;Y!v;'S#5r;'S;=`#6u<%lO#5rV#;]VOz#5rz{#6U{!g#5r!g!h#;r!h;'S#5r;'S;=`#6u<%lO#5rV#;uVOz#5rz{#6U{!n#5r!n!o#<[!o;'S#5r;'S;=`#6u<%lO#5rV#<_VOz#5rz{#6U{!k#5r!k!l#<t!l;'S#5r;'S;=`#6u<%lO#5rV#<wVOz#5rz{#6U{!p#5r!p!q#=^!q;'S#5r;'S;=`#6u<%lO#5rV#=aVOz#5rz{#6U{!g#5r!g!h#=v!h;'S#5r;'S;=`#6u<%lO#5rV#=yYOX#5rXY#>iYZ#?ZZp#5rpq#>iqz#5rz{#6U{;'S#5r;'S;=`#6u<%lO#5rV#>nXUROX#5rXY#>iYp#5rpq#>iqz#5rz{#6U{;'S#5r;'S;=`#6u<%lO#5rV#?`TUROz#5rz{#6U{;'S#5r;'S;=`#6u<%lO#5rV#?rVOz#5rz{#6U{!z#5r!z!{#@X!{;'S#5r;'S;=`#6u<%lO#5rV#@[VOz#5rz{#6U{!g#5r!g!h#@q!h;'S#5r;'S;=`#6u<%lO#5rV#@tVOz#5rz{#6U{!e#5r!e!f#=v!f;'S#5r;'S;=`#6u<%lO#5rV#A^VOz#5rz{#6U{!k#5r!k!l#As!l;'S#5r;'S;=`#6u<%lO#5rV#AvVOz#5rz{#6U{!i#5r!i!j#B]!j;'S#5r;'S;=`#6u<%lO#5rV#B`VOz#5rz{#6U{!t#5r!t!u#Bu!u;'S#5r;'S;=`#6u<%lO#5rV#BxVOz#5rz{#6U{!c#5r!c!d#C_!d;'S#5r;'S;=`#6u<%lO#5rV#CbVOz#5rz{#6U{!v#5r!v!w#=^!w;'S#5r;'S;=`#6u<%lO#5rV#CzVOz#5rz{#6U{!w#5r!w!x#Da!x;'S#5r;'S;=`#6u<%lO#5rV#DdVOz#5rz{#6U{!g#5r!g!h#Dy!h;'S#5r;'S;=`#6u<%lO#5rV#D|VOz#5rz{#6U{!t#5r!t!u#Ec!u;'S#5r;'S;=`#6u<%lO#5rV#EfVOz#5rz{#6U{!{#5r!{!|#=v!|;'S#5r;'S;=`#6u<%lO#5rV#FOXOz#5rz{#6U{!c#5r!c!d#Fk!d!g#5r!g!h#Gm!h;'S#5r;'S;=`#6u<%lO#5rV#FnVOz#5rz{#6U{!d#5r!d!e#GT!e;'S#5r;'S;=`#6u<%lO#5rV#GWVOz#5rz{#6U{!n#5r!n!o#=^!o;'S#5r;'S;=`#6u<%lO#5rV#GpVOz#5rz{#6U{!u#5r!u!v#HV!v;'S#5r;'S;=`#6u<%lO#5rV#HYVOz#5rz{#6U{!v#5r!v!w#Ho!w;'S#5r;'S;=`#6u<%lO#5rV#HrVOz#5rz{#6U{!f#5r!f!g#IX!g;'S#5r;'S;=`#6u<%lO#5rV#I[VOz#5rz{#6U{!c#5r!c!d#Iq!d;'S#5r;'S;=`#6u<%lO#5rV#ItVOz#5rz{#6U{!v#5r!v!w#JZ!w;'S#5r;'S;=`#6u<%lO#5rV#J^VOz#5rz{#6U{!c#5r!c!d#=v!d;'S#5r;'S;=`#6u<%lO#5rV#Jz]nS_QOX#KsXZ#LqZ]#Ks]^#Lq^p#Kspq#Lqqt#Kstu#Lquz#Ksz{#Mx{;'S#Ks;'S;=`$ j<%lO#KsR#Kx]_QOX#KsXZ#LqZ]#Ks]^#Lq^p#Kspq#Lqqt#Kstu#Lquz#Ksz{#Mx{;'S#Ks;'S;=`$ j<%lO#KsR#LtTOz#Lqz{#MT{;'S#Lq;'S;=`#Mr<%lO#LqR#MWVOz#Lqz{#MT{!P#Lq!P!Q#Mm!Q;'S#Lq;'S;=`#Mr<%lO#LqR#MrORRR#MuP;=`<%l#LqR#M}__QOX#KsXZ#LqZ]#Ks]^#Lq^p#Kspq#Lqqt#Kstu#Lquz#Ksz{#Mx{!P#Ks!P!Q#N|!Q;'S#Ks;'S;=`$ j<%lO#KsR$ TVRR_QOX'PZ]'P^p'Pqt'Pu;'S'P;'S;=`'k<%lO'PR$ mP;=`<%l#KsV$ u__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!c#4t!c!d$!t!d;'S#4t;'S;=`#8o<%lO#4tV$!y__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!u#4t!u!v$#x!v;'S#4t;'S;=`#8o<%lO#4tV$#}__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!g#4t!g!h$$|!h;'S#4t;'S;=`#8o<%lO#4tV$%R__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!n#4t!n!o$&Q!o;'S#4t;'S;=`#8o<%lO#4tV$&V__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!k#4t!k!l$'U!l;'S#4t;'S;=`#8o<%lO#4tV$'Z__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!p#4t!p!q$(Y!q;'S#4t;'S;=`#8o<%lO#4tV$(___QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!g#4t!g!h$)^!h;'S#4t;'S;=`#8o<%lO#4tV$)c^_QOX#4tXY#>iYZ#?ZZ]#4t]^#5r^p#4tpq#>iqt#4ttu#5ruz#4tz{#6{{;'S#4t;'S;=`#8o<%lO#4tV$*d__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!z#4t!z!{$+c!{;'S#4t;'S;=`#8o<%lO#4tV$+h__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!g#4t!g!h$,g!h;'S#4t;'S;=`#8o<%lO#4tV$,l__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!e#4t!e!f$)^!f;'S#4t;'S;=`#8o<%lO#4tV$-p__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!k#4t!k!l$.o!l;'S#4t;'S;=`#8o<%lO#4tV$.t__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!i#4t!i!j$/s!j;'S#4t;'S;=`#8o<%lO#4tV$/x__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!t#4t!t!u$0w!u;'S#4t;'S;=`#8o<%lO#4tV$0|__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!c#4t!c!d$1{!d;'S#4t;'S;=`#8o<%lO#4tV$2Q__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!v#4t!v!w$(Y!w;'S#4t;'S;=`#8o<%lO#4tV$3U__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!w#4t!w!x$4T!x;'S#4t;'S;=`#8o<%lO#4tV$4Y__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!g#4t!g!h$5X!h;'S#4t;'S;=`#8o<%lO#4tV$5^__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!t#4t!t!u$6]!u;'S#4t;'S;=`#8o<%lO#4tV$6b__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!{#4t!{!|$)^!|;'S#4t;'S;=`#8o<%lO#4tV$7fa_QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!c#4t!c!d$8k!d!g#4t!g!h$:s!h;'S#4t;'S;=`#8o<%lO#4tV$8p__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!d#4t!d!e$9o!e;'S#4t;'S;=`#8o<%lO#4tV$9t__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!n#4t!n!o$(Y!o;'S#4t;'S;=`#8o<%lO#4tV$:x__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!u#4t!u!v$;w!v;'S#4t;'S;=`#8o<%lO#4tV$;|__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!v#4t!v!w$<{!w;'S#4t;'S;=`#8o<%lO#4tV$=Q__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!f#4t!f!g$>P!g;'S#4t;'S;=`#8o<%lO#4tV$>U__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!c#4t!c!d$?T!d;'S#4t;'S;=`#8o<%lO#4tV$?Y__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!v#4t!v!w$@X!w;'S#4t;'S;=`#8o<%lO#4tV$@^__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!c#4t!c!d$)^!d;'S#4t;'S;=`#8o<%lO#4tV$AdeVP_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{!Q#Y!Q![$A]![!c#Y!c!}$A]!}#R#Y#R#S$A]#S#T#Y#T#o$A]#o;'S#Y;'S;=`'q<%lO#Y~$Bze_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{!Q#Y!Q![$D]![!c#Y!c!}$D]!}#R#Y#R#S$D]#S#T#Y#T#o$D]#o;'S#Y;'S;=`'q<%lO#Y~$DdeW~_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{!Q#Y!Q![$D]![!c#Y!c!}$D]!}#R#Y#R#S$D]#S#T#Y#T#o$D]#o;'S#Y;'S;=`'q<%lO#YV$E|]qP_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{;'S#Y;'S;=`'q<%lO#YU$Fz__QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{#g#Y#g#h$Gy#h;'S#Y;'S;=`'q<%lO#YU$HO__QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{#X#Y#X#Y$H}#Y;'S#Y;'S;=`'q<%lO#YU$IS__QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{#h#Y#h#i$JR#i;'S#Y;'S;=`'q<%lO#YU$JY]pQ_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{;'S#Y;'S;=`'q<%lO#Y",
|
|
417
|
+
tokenData: "$NX~RqOX#YXY'wYZ(iZ]#Y]^$W^p#Ypq'wqr#Yrs(}st#Ytu6^uw#Ywx9[xz#Yz{%_{}#Y}!OKi!O!P#Y!P!Q#2P!Q![$A]![!]$Bu!]!_#Y!_!`$H{!`!b#Y!b!c$I{!c!}$A]!}#R#Y#R#S$A]#S#T#Y#T#o$A]#o;'S#Y;'S;=`'q<%lO#YU#_]_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{;'S#Y;'S;=`'q<%lO#YS$ZTOz$Wz{$j{;'S$W;'S;=`%X<%lO$WS$mVOz$Wz{$j{!P$W!P!Q%S!Q;'S$W;'S;=`%X<%lO$WS%XOnSS%[P;=`<%l$WU%d__QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{!P#Y!P!Q&c!Q;'S#Y;'S;=`'q<%lO#YU&jVnS_QOX'PZ]'P^p'Pqt'Pu;'S'P;'S;=`'k<%lO'PQ'UV_QOX'PZ]'P^p'Pqt'Pu;'S'P;'S;=`'k<%lO'PQ'nP;=`<%l'PU'tP;=`<%l#Y~'|Xj~OX$WXY'wYp$Wpq'wqz$Wz{$j{;'S$W;'S;=`%X<%lO$W~(nTk~Oz$Wz{$j{;'S$W;'S;=`%X<%lO$WU)Sb_QOX(}XY*[YZ$WZ](}]^*[^p(}pq*[qr(}rs.yst(}tu*[uz(}z{/y{#O(}#O#P5V#P;'S(};'S;=`6W<%lO(}U*_ZOY*[YZ$WZr*[rs+Qsz*[z{+f{#O*[#O#P.Z#P;'S*[;'S;=`.s<%lO*[U+VT]QOz$Wz{$j{;'S$W;'S;=`%X<%lO$WU+i]OY*[YZ$WZr*[rs+Qsz*[z{+f{!P*[!P!Q,b!Q#O*[#O#P.Z#P;'S*[;'S;=`.s<%lO*[U,gWnSOY-PZr-Prs-ls#O-P#O#P-q#P;'S-P;'S;=`.T<%lO-PQ-SWOY-PZr-Prs-ls#O-P#O#P-q#P;'S-P;'S;=`.T<%lO-PQ-qO]QQ-tTOY-PYZ-PZ;'S-P;'S;=`.T<%lO-PQ.WP;=`<%l-PU.^VOY*[YZ*[Zz*[z{+f{;'S*[;'S;=`.s<%lO*[U.vP;=`<%l*[U/Q]]Q_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{;'S#Y;'S;=`'q<%lO#YU0Od_QOX(}XY*[YZ$WZ](}]^*[^p(}pq*[qr(}rs.yst(}tu*[uz(}z{/y{!P(}!P!Q1^!Q#O(}#O#P5V#P;'S(};'S;=`6W<%lO(}U1e_nS_QOX2dXY-PZ]2d]^-P^p2dpq-Pqr2drs3hst2dtu-Pu#O2d#O#P4U#P;'S2d;'S;=`5P<%lO2dQ2i__QOX2dXY-PZ]2d]^-P^p2dpq-Pqr2drs3hst2dtu-Pu#O2d#O#P4U#P;'S2d;'S;=`5P<%lO2dQ3oV]Q_QOX'PZ]'P^p'Pqt'Pu;'S'P;'S;=`'k<%lO'PQ4Z[_QOX2dXY-PYZ-PZ]2d]^-P^p2dpq-Pqt2dtu-Pu;'S2d;'S;=`5P<%lO2dQ5SP;=`<%l2dU5[^_QOX(}XY*[YZ*[Z](}]^*[^p(}pq*[qt(}tu*[uz(}z{/y{;'S(};'S;=`6W<%lO(}U6ZP;=`<%l(}U6cV_QOz$Wz{$j{#o$W#o#p6x#p;'S$W;'S;=`%X<%lO$WU6{]Oz$Wz{$j{!Q$W!Q![7t![!c$W!c!}7t!}#R$W#R#S7t#S#T$W#T#o7t#o;'S$W;'S;=`%X<%lO$WU7w_Oz$Wz{$j{!Q$W!Q![7t![!c$W!c!}7t!}#R$W#R#S7t#S#T$W#T#o7t#o#q$W#q#r8v#r;'S$W;'S;=`%X<%lO$WU8{TaQOz$Wz{$j{;'S$W;'S;=`%X<%lO$WU9ab_QOX9[XY:iYZ$WZ]9[]^:i^p9[pq:iqt9[tu:iuw9[wxAVxz9[z{BV{#O9[#O#PHu#P;'S9[;'S;=`Kc<%lO9[U:lZOY:iYZ$WZw:iwx;_xz:iz{;s{#O:i#O#P?c#P;'S:i;'S;=`AP<%lO:iU;dT^QOz$Wz{$j{;'S$W;'S;=`%X<%lO$WU;v]OY:iYZ$WZw:iwx;_xz:iz{;s{!P:i!P!Q<o!Q#O:i#O#P?c#P;'S:i;'S;=`AP<%lO:iU<tWnSOY=^Zw=^wx=yx#O=^#O#P>O#P;'S=^;'S;=`?]<%lO=^Q=aWOY=^Zw=^wx=yx#O=^#O#P>O#P;'S=^;'S;=`?]<%lO=^Q>OO^QQ>RXOY=^YZ=^Zw=^wx>nx#O=^#O#P>O#P;'S=^;'S;=`?]<%lO=^Q>sW^QOY=^Zw=^wx=yx#O=^#O#P>O#P;'S=^;'S;=`?]<%lO=^Q?`P;=`<%l=^U?fZOY:iYZ:iZw:iwx@Xxz:iz{;s{#O:i#O#P?c#P;'S:i;'S;=`AP<%lO:iU@^Z^QOY:iYZ$WZw:iwx;_xz:iz{;s{#O:i#O#P?c#P;'S:i;'S;=`AP<%lO:iUASP;=`<%l:iUA^]^Q_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{;'S#Y;'S;=`'q<%lO#YUB[d_QOX9[XY:iYZ$WZ]9[]^:i^p9[pq:iqt9[tu:iuw9[wxAVxz9[z{BV{!P9[!P!QCj!Q#O9[#O#PHu#P;'S9[;'S;=`Kc<%lO9[UCq_nS_QOXDpXY=^Z]Dp]^=^^pDppq=^qtDptu=^uwDpwxEtx#ODp#O#PFb#P;'SDp;'S;=`Ho<%lODpQDu__QOXDpXY=^Z]Dp]^=^^pDppq=^qtDptu=^uwDpwxEtx#ODp#O#PFb#P;'SDp;'S;=`Ho<%lODpQE{V^Q_QOX'PZ]'P^p'Pqt'Pu;'S'P;'S;=`'k<%lO'PQFg`_QOXDpXY=^YZ=^Z]Dp]^=^^pDppq=^qtDptu=^uwDpwxGix#ODp#O#PFb#P;'SDp;'S;=`Ho<%lODpQGp_^Q_QOXDpXY=^Z]Dp]^=^^pDppq=^qtDptu=^uwDpwxEtx#ODp#O#PFb#P;'SDp;'S;=`Ho<%lODpQHrP;=`<%lDpUHzb_QOX9[XY:iYZ:iZ]9[]^:i^p9[pq:iqt9[tu:iuw9[wxJSxz9[z{BV{#O9[#O#PHu#P;'S9[;'S;=`Kc<%lO9[UJZb^Q_QOX9[XY:iYZ$WZ]9[]^:i^p9[pq:iqt9[tu:iuw9[wxAVxz9[z{BV{#O9[#O#PHu#P;'S9[;'S;=`Kc<%lO9[UKfP;=`<%l9[VKn__QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{}#Y}!OLm!O;'S#Y;'S;=`'q<%lO#YVLthQR_QOXN`XY!&|YZ$WZ]N`]^! c^pN`pq!&|qtN`tu! cuzN`z{!#q{!dN`!d!e!=`!e!gN`!g!h!Ft!h!oN`!o!p!Ja!p!sN`!s!t#!`!t!vN`!v!w#'U!w;'SN`;'S;=`!&v<%lON`VNg^QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{;'SN`;'S;=`!&v<%lON`V! hVQROY! cYZ$WZz! cz{! }{;'S! c;'S;=`!#k<%lO! cV!!SXQROY! cYZ$WZz! cz{! }{!P! c!P!Q!!o!Q;'S! c;'S;=`!#k<%lO! cV!!vSnSQROY!#SZ;'S!#S;'S;=`!#e<%lO!#SR!#XSQROY!#SZ;'S!#S;'S;=`!#e<%lO!#SR!#hP;=`<%l!#SV!#nP;=`<%l! cV!#x`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!PN`!P!Q!$z!Q;'SN`;'S;=`!&v<%lON`V!%TZnSQR_QOX!%vXY!#SZ]!%v]^!#S^p!%vpq!#Sqt!%vtu!#Su;'S!%v;'S;=`!&p<%lO!%vR!%}ZQR_QOX!%vXY!#SZ]!%v]^!#S^p!%vpq!#Sqt!%vtu!#Su;'S!%v;'S;=`!&p<%lO!%vR!&sP;=`<%l!%vV!&yP;=`<%lN`V!'RdQROX! cXY!&|YZ$WZp! cpq!&|qz! cz{! }{!d! c!d!e!(a!e!g! c!g!h!.l!h!o! c!o!p!0p!p!s! c!s!t!4W!t!v! c!v!w!6|!w;'S! c;'S;=`!#k<%lO! cV!(fXQROY! cYZ$WZz! cz{! }{!c! c!c!d!)R!d;'S! c;'S;=`!#k<%lO! cV!)WXQROY! cYZ$WZz! cz{! }{!u! c!u!v!)s!v;'S! c;'S;=`!#k<%lO! cV!)xXQROY! cYZ$WZz! cz{! }{!g! c!g!h!*e!h;'S! c;'S;=`!#k<%lO! cV!*jXQROY! cYZ$WZz! cz{! }{!n! c!n!o!+V!o;'S! c;'S;=`!#k<%lO! cV!+[XQROY! cYZ$WZz! cz{! }{!k! c!k!l!+w!l;'S! c;'S;=`!#k<%lO! cV!+|XQROY! cYZ$WZz! cz{! }{!p! c!p!q!,i!q;'S! c;'S;=`!#k<%lO! cV!,nXQROY! cYZ$WZz! cz{! }{!g! c!g!h!-Z!h;'S! c;'S;=`!#k<%lO! cV!-`YQROX! cXY!.OYZ$WZp! cpq!.Oqz! cz{! }{;'S! c;'S;=`!#k<%lO! cV!.VVYRQROY! cYZ$WZz! cz{! }{;'S! c;'S;=`!#k<%lO! cV!.qXQROY! cYZ$WZz! cz{! }{!z! c!z!{!/^!{;'S! c;'S;=`!#k<%lO! cV!/cXQROY! cYZ$WZz! cz{! }{!g! c!g!h!0O!h;'S! c;'S;=`!#k<%lO! cV!0TXQROY! cYZ$WZz! cz{! }{!e! c!e!f!-Z!f;'S! c;'S;=`!#k<%lO! cV!0uXQROY! cYZ$WZz! cz{! }{!k! c!k!l!1b!l;'S! c;'S;=`!#k<%lO! cV!1gXQROY! cYZ$WZz! cz{! }{!i! c!i!j!2S!j;'S! c;'S;=`!#k<%lO! cV!2XXQROY! cYZ$WZz! cz{! }{!t! c!t!u!2t!u;'S! c;'S;=`!#k<%lO! cV!2yXQROY! cYZ$WZz! cz{! }{!c! c!c!d!3f!d;'S! c;'S;=`!#k<%lO! cV!3kXQROY! cYZ$WZz! cz{! }{!v! c!v!w!,i!w;'S! c;'S;=`!#k<%lO! cV!4]XQROY! cYZ$WZz! cz{! }{!w! c!w!x!4x!x;'S! c;'S;=`!#k<%lO! cV!4}XQROY! cYZ$WZz! cz{! }{!g! c!g!h!5j!h;'S! c;'S;=`!#k<%lO! cV!5oXQROY! cYZ$WZz! cz{! }{!t! c!t!u!6[!u;'S! c;'S;=`!#k<%lO! cV!6aXQROY! cYZ$WZz! cz{! }{!{! c!{!|!-Z!|;'S! c;'S;=`!#k<%lO! cV!7RZQROY! cYZ$WZz! cz{! }{!c! c!c!d!7t!d!g! c!g!h!9W!h;'S! c;'S;=`!#k<%lO! cV!7yXQROY! cYZ$WZz! cz{! }{!d! c!d!e!8f!e;'S! c;'S;=`!#k<%lO! cV!8kXQROY! cYZ$WZz! cz{! }{!n! c!n!o!,i!o;'S! c;'S;=`!#k<%lO! cV!9]XQROY! cYZ$WZz! cz{! }{!u! c!u!v!9x!v;'S! c;'S;=`!#k<%lO! cV!9}XQROY! cYZ$WZz! cz{! }{!v! c!v!w!:j!w;'S! c;'S;=`!#k<%lO! cV!:oXQROY! cYZ$WZz! cz{! }{!f! c!f!g!;[!g;'S! c;'S;=`!#k<%lO! cV!;aXQROY! cYZ$WZz! cz{! }{!c! c!c!d!;|!d;'S! c;'S;=`!#k<%lO! cV!<RXQROY! cYZ$WZz! cz{! }{!v! c!v!w!<n!w;'S! c;'S;=`!#k<%lO! cV!<sXQROY! cYZ$WZz! cz{! }{!c! c!c!d!-Z!d;'S! c;'S;=`!#k<%lO! cV!=g`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!cN`!c!d!>i!d;'SN`;'S;=`!&v<%lON`V!>p`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!uN`!u!v!?r!v;'SN`;'S;=`!&v<%lON`V!?y`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!gN`!g!h!@{!h;'SN`;'S;=`!&v<%lON`V!AS`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!nN`!n!o!BU!o;'SN`;'S;=`!&v<%lON`V!B]`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!kN`!k!l!C_!l;'SN`;'S;=`!&v<%lON`V!Cf`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!pN`!p!q!Dh!q;'SN`;'S;=`!&v<%lON`V!Do`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!gN`!g!h!Eq!h;'SN`;'S;=`!&v<%lON`V!Ex^QR_QOXN`XY!.OYZ$WZ]N`]^! c^pN`pq!.OqtN`tu! cuzN`z{!#q{;'SN`;'S;=`!&v<%lON`V!F{`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!zN`!z!{!G}!{;'SN`;'S;=`!&v<%lON`V!HU`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!gN`!g!h!IW!h;'SN`;'S;=`!&v<%lON`V!I_`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!eN`!e!f!Eq!f;'SN`;'S;=`!&v<%lON`V!Jh`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!kN`!k!l!Kj!l;'SN`;'S;=`!&v<%lON`V!Kq`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!iN`!i!j!Ls!j;'SN`;'S;=`!&v<%lON`V!Lz`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!tN`!t!u!M|!u;'SN`;'S;=`!&v<%lON`V!NT`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!cN`!c!d# V!d;'SN`;'S;=`!&v<%lON`V# ^`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!vN`!v!w!Dh!w;'SN`;'S;=`!&v<%lON`V#!g`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!wN`!w!x##i!x;'SN`;'S;=`!&v<%lON`V##p`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!gN`!g!h#$r!h;'SN`;'S;=`!&v<%lON`V#$y`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!tN`!t!u#%{!u;'SN`;'S;=`!&v<%lON`V#&S`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!{N`!{!|!Eq!|;'SN`;'S;=`!&v<%lON`V#']bQR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!cN`!c!d#(e!d!gN`!g!h#*w!h;'SN`;'S;=`!&v<%lON`V#(l`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!dN`!d!e#)n!e;'SN`;'S;=`!&v<%lON`V#)u`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!nN`!n!o!Dh!o;'SN`;'S;=`!&v<%lON`V#+O`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!uN`!u!v#,Q!v;'SN`;'S;=`!&v<%lON`V#,X`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!vN`!v!w#-Z!w;'SN`;'S;=`!&v<%lON`V#-b`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!fN`!f!g#.d!g;'SN`;'S;=`!&v<%lON`V#.k`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!cN`!c!d#/m!d;'SN`;'S;=`!&v<%lON`V#/t`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!vN`!v!w#0v!w;'SN`;'S;=`!&v<%lON`V#0}`QR_QOXN`XY! cYZ$WZ]N`]^! c^pN`pq! cqtN`tu! cuzN`z{!#q{!cN`!c!d!Eq!d;'SN`;'S;=`!&v<%lON`V#2U]_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{#2}{;'S#Y;'S;=`'q<%lO#YV#3Sj_QOX#4tXY#8uYZ#8uZ]#4t]^#5r^p#4tpq#8uqt#4ttu#5ruz#4tz{#6{{!P#4t!P!Q#Js!Q!d#4t!d!e$ p!e!g#4t!g!h$*_!h!o#4t!o!p$-k!p!s#4t!s!t$3P!t!v#4t!v!w$7a!w;'S#4t;'S;=`#8o<%lO#4tV#4y]_QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{;'S#4t;'S;=`#8o<%lO#4tV#5uTOz#5rz{#6U{;'S#5r;'S;=`#6u<%lO#5rV#6XVOz#5rz{#6U{!P#5r!P!Q#6n!Q;'S#5r;'S;=`#6u<%lO#5rV#6uORRnSV#6xP;=`<%l#5rV#7Q__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!P#4t!P!Q#8P!Q;'S#4t;'S;=`#8o<%lO#4tV#8YVRRnS_QOX'PZ]'P^p'Pqt'Pu;'S'P;'S;=`'k<%lO'PV#8rP;=`<%l#4tV#8xdOX#5rXY#8uYZ#8uZp#5rpq#8uqz#5rz{#6U{!d#5r!d!e#:W!e!g#5r!g!h#?o!h!o#5r!o!p#AZ!p!s#5r!s!t#Cw!t!v#5r!v!w#E{!w;'S#5r;'S;=`#6u<%lO#5rV#:ZVOz#5rz{#6U{!c#5r!c!d#:p!d;'S#5r;'S;=`#6u<%lO#5rV#:sVOz#5rz{#6U{!u#5r!u!v#;Y!v;'S#5r;'S;=`#6u<%lO#5rV#;]VOz#5rz{#6U{!g#5r!g!h#;r!h;'S#5r;'S;=`#6u<%lO#5rV#;uVOz#5rz{#6U{!n#5r!n!o#<[!o;'S#5r;'S;=`#6u<%lO#5rV#<_VOz#5rz{#6U{!k#5r!k!l#<t!l;'S#5r;'S;=`#6u<%lO#5rV#<wVOz#5rz{#6U{!p#5r!p!q#=^!q;'S#5r;'S;=`#6u<%lO#5rV#=aVOz#5rz{#6U{!g#5r!g!h#=v!h;'S#5r;'S;=`#6u<%lO#5rV#=yYOX#5rXY#>iYZ#?ZZp#5rpq#>iqz#5rz{#6U{;'S#5r;'S;=`#6u<%lO#5rV#>nXUROX#5rXY#>iYp#5rpq#>iqz#5rz{#6U{;'S#5r;'S;=`#6u<%lO#5rV#?`TUROz#5rz{#6U{;'S#5r;'S;=`#6u<%lO#5rV#?rVOz#5rz{#6U{!z#5r!z!{#@X!{;'S#5r;'S;=`#6u<%lO#5rV#@[VOz#5rz{#6U{!g#5r!g!h#@q!h;'S#5r;'S;=`#6u<%lO#5rV#@tVOz#5rz{#6U{!e#5r!e!f#=v!f;'S#5r;'S;=`#6u<%lO#5rV#A^VOz#5rz{#6U{!k#5r!k!l#As!l;'S#5r;'S;=`#6u<%lO#5rV#AvVOz#5rz{#6U{!i#5r!i!j#B]!j;'S#5r;'S;=`#6u<%lO#5rV#B`VOz#5rz{#6U{!t#5r!t!u#Bu!u;'S#5r;'S;=`#6u<%lO#5rV#BxVOz#5rz{#6U{!c#5r!c!d#C_!d;'S#5r;'S;=`#6u<%lO#5rV#CbVOz#5rz{#6U{!v#5r!v!w#=^!w;'S#5r;'S;=`#6u<%lO#5rV#CzVOz#5rz{#6U{!w#5r!w!x#Da!x;'S#5r;'S;=`#6u<%lO#5rV#DdVOz#5rz{#6U{!g#5r!g!h#Dy!h;'S#5r;'S;=`#6u<%lO#5rV#D|VOz#5rz{#6U{!t#5r!t!u#Ec!u;'S#5r;'S;=`#6u<%lO#5rV#EfVOz#5rz{#6U{!{#5r!{!|#=v!|;'S#5r;'S;=`#6u<%lO#5rV#FOXOz#5rz{#6U{!c#5r!c!d#Fk!d!g#5r!g!h#Gm!h;'S#5r;'S;=`#6u<%lO#5rV#FnVOz#5rz{#6U{!d#5r!d!e#GT!e;'S#5r;'S;=`#6u<%lO#5rV#GWVOz#5rz{#6U{!n#5r!n!o#=^!o;'S#5r;'S;=`#6u<%lO#5rV#GpVOz#5rz{#6U{!u#5r!u!v#HV!v;'S#5r;'S;=`#6u<%lO#5rV#HYVOz#5rz{#6U{!v#5r!v!w#Ho!w;'S#5r;'S;=`#6u<%lO#5rV#HrVOz#5rz{#6U{!f#5r!f!g#IX!g;'S#5r;'S;=`#6u<%lO#5rV#I[VOz#5rz{#6U{!c#5r!c!d#Iq!d;'S#5r;'S;=`#6u<%lO#5rV#ItVOz#5rz{#6U{!v#5r!v!w#JZ!w;'S#5r;'S;=`#6u<%lO#5rV#J^VOz#5rz{#6U{!c#5r!c!d#=v!d;'S#5r;'S;=`#6u<%lO#5rV#Jz]nS_QOX#KsXZ#LqZ]#Ks]^#Lq^p#Kspq#Lqqt#Kstu#Lquz#Ksz{#Mx{;'S#Ks;'S;=`$ j<%lO#KsR#Kx]_QOX#KsXZ#LqZ]#Ks]^#Lq^p#Kspq#Lqqt#Kstu#Lquz#Ksz{#Mx{;'S#Ks;'S;=`$ j<%lO#KsR#LtTOz#Lqz{#MT{;'S#Lq;'S;=`#Mr<%lO#LqR#MWVOz#Lqz{#MT{!P#Lq!P!Q#Mm!Q;'S#Lq;'S;=`#Mr<%lO#LqR#MrORRR#MuP;=`<%l#LqR#M}__QOX#KsXZ#LqZ]#Ks]^#Lq^p#Kspq#Lqqt#Kstu#Lquz#Ksz{#Mx{!P#Ks!P!Q#N|!Q;'S#Ks;'S;=`$ j<%lO#KsR$ TVRR_QOX'PZ]'P^p'Pqt'Pu;'S'P;'S;=`'k<%lO'PR$ mP;=`<%l#KsV$ u__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!c#4t!c!d$!t!d;'S#4t;'S;=`#8o<%lO#4tV$!y__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!u#4t!u!v$#x!v;'S#4t;'S;=`#8o<%lO#4tV$#}__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!g#4t!g!h$$|!h;'S#4t;'S;=`#8o<%lO#4tV$%R__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!n#4t!n!o$&Q!o;'S#4t;'S;=`#8o<%lO#4tV$&V__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!k#4t!k!l$'U!l;'S#4t;'S;=`#8o<%lO#4tV$'Z__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!p#4t!p!q$(Y!q;'S#4t;'S;=`#8o<%lO#4tV$(___QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!g#4t!g!h$)^!h;'S#4t;'S;=`#8o<%lO#4tV$)c^_QOX#4tXY#>iYZ#?ZZ]#4t]^#5r^p#4tpq#>iqt#4ttu#5ruz#4tz{#6{{;'S#4t;'S;=`#8o<%lO#4tV$*d__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!z#4t!z!{$+c!{;'S#4t;'S;=`#8o<%lO#4tV$+h__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!g#4t!g!h$,g!h;'S#4t;'S;=`#8o<%lO#4tV$,l__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!e#4t!e!f$)^!f;'S#4t;'S;=`#8o<%lO#4tV$-p__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!k#4t!k!l$.o!l;'S#4t;'S;=`#8o<%lO#4tV$.t__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!i#4t!i!j$/s!j;'S#4t;'S;=`#8o<%lO#4tV$/x__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!t#4t!t!u$0w!u;'S#4t;'S;=`#8o<%lO#4tV$0|__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!c#4t!c!d$1{!d;'S#4t;'S;=`#8o<%lO#4tV$2Q__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!v#4t!v!w$(Y!w;'S#4t;'S;=`#8o<%lO#4tV$3U__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!w#4t!w!x$4T!x;'S#4t;'S;=`#8o<%lO#4tV$4Y__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!g#4t!g!h$5X!h;'S#4t;'S;=`#8o<%lO#4tV$5^__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!t#4t!t!u$6]!u;'S#4t;'S;=`#8o<%lO#4tV$6b__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!{#4t!{!|$)^!|;'S#4t;'S;=`#8o<%lO#4tV$7fa_QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!c#4t!c!d$8k!d!g#4t!g!h$:s!h;'S#4t;'S;=`#8o<%lO#4tV$8p__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!d#4t!d!e$9o!e;'S#4t;'S;=`#8o<%lO#4tV$9t__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!n#4t!n!o$(Y!o;'S#4t;'S;=`#8o<%lO#4tV$:x__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!u#4t!u!v$;w!v;'S#4t;'S;=`#8o<%lO#4tV$;|__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!v#4t!v!w$<{!w;'S#4t;'S;=`#8o<%lO#4tV$=Q__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!f#4t!f!g$>P!g;'S#4t;'S;=`#8o<%lO#4tV$>U__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!c#4t!c!d$?T!d;'S#4t;'S;=`#8o<%lO#4tV$?Y__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!v#4t!v!w$@X!w;'S#4t;'S;=`#8o<%lO#4tV$@^__QOX#4tXZ#5rZ]#4t]^#5r^p#4tpq#5rqt#4ttu#5ruz#4tz{#6{{!c#4t!c!d$)^!d;'S#4t;'S;=`#8o<%lO#4tV$AdeVP_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{!Q#Y!Q![$A]![!c#Y!c!}$A]!}#R#Y#R#S$A]#S#T#Y#T#o$A]#o;'S#Y;'S;=`'q<%lO#Y~$Bze_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{!Q#Y!Q![$D]![!c#Y!c!}$D]!}#R#Y#R#S$D]#S#T#Y#T#o$D]#o;'S#Y;'S;=`'q<%lO#Y~$DdgW~_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{!Q#Y!Q![$D]![!_#Y!_!`$E{!`!c#Y!c!}$D]!}#R#Y#R#S$D]#S#T#Y#T#o$D]#o;'S#Y;'S;=`'q<%lO#Y~$FQe_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{!Q#Y!Q![$Gc![!c#Y!c!}$Gc!}#R#Y#R#S$Gc#S#T#Y#T#o$Gc#o;'S#Y;'S;=`'q<%lO#Y~$GjeW~_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{!Q#Y!Q![$Gc![!c#Y!c!}$Gc!}#R#Y#R#S$Gc#S#T#Y#T#o$Gc#o;'S#Y;'S;=`'q<%lO#YV$IS]qP_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{;'S#Y;'S;=`'q<%lO#YU$JQ__QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{#g#Y#g#h$KP#h;'S#Y;'S;=`'q<%lO#YU$KU__QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{#X#Y#X#Y$LT#Y;'S#Y;'S;=`'q<%lO#YU$LY__QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{#h#Y#h#i$MX#i;'S#Y;'S;=`'q<%lO#YU$M`]pQ_QOX#YXZ$WZ]#Y]^$W^p#Ypq$Wqt#Ytu$Wuz#Yz{%_{;'S#Y;'S;=`'q<%lO#Y",
|
|
409
418
|
tokenizers: [
|
|
410
419
|
0,
|
|
411
420
|
1,
|
|
@@ -459,13 +468,13 @@ var TypeMapper = class {
|
|
|
459
468
|
* @param path - Optional prefix path for nested type references
|
|
460
469
|
* @returns Generated type declaration code, or empty string for primitive types
|
|
461
470
|
*/
|
|
462
|
-
getDeclarations(column, path = "") {
|
|
463
|
-
if (column.type instanceof StructType) return this.generateStructDeclaration(column, path);
|
|
471
|
+
getDeclarations(column, path = "", nameOverride) {
|
|
472
|
+
if (column.type instanceof StructType) return this.generateStructDeclaration(column, path, nameOverride);
|
|
464
473
|
if (column.type instanceof ListType) return this.getDeclarations({
|
|
465
474
|
name: column.name,
|
|
466
475
|
type: column.type.baseType,
|
|
467
476
|
nullable: true
|
|
468
|
-
}, path);
|
|
477
|
+
}, path, nameOverride);
|
|
469
478
|
return "";
|
|
470
479
|
}
|
|
471
480
|
};
|
|
@@ -622,9 +631,9 @@ var JavaTypeMapper = class JavaTypeMapper extends TypeMapper {
|
|
|
622
631
|
formatStructTypeName(fieldName) {
|
|
623
632
|
return `${pascalCase(fieldName)}Result`;
|
|
624
633
|
}
|
|
625
|
-
generateStructDeclaration(column, path = "") {
|
|
634
|
+
generateStructDeclaration(column, path = "", nameOverride) {
|
|
626
635
|
if (!(column.type instanceof StructType)) throw new Error(`Expected StructType ${column}`);
|
|
627
|
-
const structName = this.formatStructTypeName(column.name);
|
|
636
|
+
const structName = nameOverride ?? this.formatStructTypeName(column.name);
|
|
628
637
|
const newPath = `${path}${structName}.`;
|
|
629
638
|
const children = column.type.fields.map((field) => {
|
|
630
639
|
return this.getDeclarations({
|
|
@@ -818,7 +827,7 @@ var TypeScriptTypeMapper = class extends TypeMapper {
|
|
|
818
827
|
formatMapTypeName(fieldName) {
|
|
819
828
|
return "Map";
|
|
820
829
|
}
|
|
821
|
-
generateStructDeclaration(column) {
|
|
830
|
+
generateStructDeclaration(column, _path, _nameOverride) {
|
|
822
831
|
if (!(column.type instanceof StructType)) throw new Error("Expected StructType");
|
|
823
832
|
return `interface ${this.formatStructTypeName(column.name)} {\n entries: {\n${column.type.fields.map((field) => {
|
|
824
833
|
const fieldType = this.getTypeName({
|
|
@@ -953,7 +962,7 @@ var PythonTypeMapper = class PythonTypeMapper extends TypeMapper {
|
|
|
953
962
|
formatMapTypeName(_fieldName) {
|
|
954
963
|
return "dict";
|
|
955
964
|
}
|
|
956
|
-
generateStructDeclaration(column, path = "") {
|
|
965
|
+
generateStructDeclaration(column, path = "", _nameOverride) {
|
|
957
966
|
if (!(column.type instanceof StructType)) throw new Error(`Expected StructType ${column}`);
|
|
958
967
|
const structName = this.formatStructTypeName(column.name);
|
|
959
968
|
const newPath = `${path}${structName}.`;
|
|
@@ -1024,7 +1033,7 @@ var BaseGenerator = class {
|
|
|
1024
1033
|
...query.columns[0],
|
|
1025
1034
|
name: query.id
|
|
1026
1035
|
});
|
|
1027
|
-
return this.getClassName(`${query.id}_Result`);
|
|
1036
|
+
return this.getClassName(query.resultTypeName ?? `${query.id}_Result`);
|
|
1028
1037
|
}
|
|
1029
1038
|
getStatement(q) {
|
|
1030
1039
|
return q.queryAnonymous;
|
|
@@ -1034,6 +1043,8 @@ var BaseGenerator = class {
|
|
|
1034
1043
|
//#region src/generators/java-generator.ts
|
|
1035
1044
|
var JavaGenerator = class extends BaseGenerator {
|
|
1036
1045
|
engine;
|
|
1046
|
+
/** Query ids that should emit their row-type record declaration (set per render). */
|
|
1047
|
+
declareTypeOwners = /* @__PURE__ */ new Set();
|
|
1037
1048
|
constructor(template, engine = "duckdb") {
|
|
1038
1049
|
super(template, new JavaTypeMapper());
|
|
1039
1050
|
this.template = template;
|
|
@@ -1084,7 +1095,21 @@ var JavaGenerator = class extends BaseGenerator {
|
|
|
1084
1095
|
if (readExpr !== null) return readExpr;
|
|
1085
1096
|
return this.typeMapper.parseValue(column, `rs.getObject(${idx})`, path);
|
|
1086
1097
|
}
|
|
1087
|
-
async beforeGenerate(_projectDir, _gen,
|
|
1098
|
+
async beforeGenerate(_projectDir, _gen, queries, tables) {
|
|
1099
|
+
this.declareTypeOwners = /* @__PURE__ */ new Set();
|
|
1100
|
+
const seenRowTypes = /* @__PURE__ */ new Set();
|
|
1101
|
+
const tableRowTypes = new Set(this.supportsAppenders(this.engine) ? tables.filter((t) => t.hasAppender).map((t) => this.getClassName(`${t.id}_row`)) : []);
|
|
1102
|
+
for (const q of queries) {
|
|
1103
|
+
if (q.skipGenerateFunction) continue;
|
|
1104
|
+
if (!q.isQuery || q.isPluck) continue;
|
|
1105
|
+
if (q.columns.length === 0) continue;
|
|
1106
|
+
const rowTypeName = this.rowType(q);
|
|
1107
|
+
if (tableRowTypes.has(rowTypeName)) continue;
|
|
1108
|
+
if (seenRowTypes.has(rowTypeName)) continue;
|
|
1109
|
+
seenRowTypes.add(rowTypeName);
|
|
1110
|
+
this.declareTypeOwners.add(q.id);
|
|
1111
|
+
}
|
|
1112
|
+
Handlebars.registerHelper("shouldDeclareType", (queryHelper) => this.declareTypeOwners.has(queryHelper.id));
|
|
1088
1113
|
Handlebars.registerHelper("isDuckDB", () => this.engine === "duckdb");
|
|
1089
1114
|
Handlebars.registerHelper("isPostgres", () => this.engine === "postgres");
|
|
1090
1115
|
Handlebars.registerHelper("pgBulkType", (column) => {
|
|
@@ -1107,7 +1132,9 @@ var JavaGenerator = class extends BaseGenerator {
|
|
|
1107
1132
|
...query.columns[0],
|
|
1108
1133
|
name: query.id
|
|
1109
1134
|
}, " ");
|
|
1110
|
-
|
|
1135
|
+
if (!this.declareTypeOwners.has(query.id)) return "";
|
|
1136
|
+
const structName = this.rowType(query);
|
|
1137
|
+
return queryHelper.typeMapper.getDeclarations(query.allColumns, "", structName);
|
|
1111
1138
|
});
|
|
1112
1139
|
Handlebars.registerHelper("appenderType", (column) => {
|
|
1113
1140
|
return this.typeMapper.getUnboxedTypeName(column);
|
|
@@ -1331,13 +1358,25 @@ var JavaDuckDBArrowGenerator = class extends BaseGenerator {
|
|
|
1331
1358
|
async beforeGenerate(projectDir, gen, queries, tables) {
|
|
1332
1359
|
const q = queries.filter((q) => q.isQuery && q.isOne || q.isMigrate);
|
|
1333
1360
|
const name = `${gen.name}-jdbc`;
|
|
1334
|
-
writeGeneratedFile(projectDir, {
|
|
1361
|
+
await writeGeneratedFile(projectDir, {
|
|
1335
1362
|
name,
|
|
1336
1363
|
generator: "java/duckdb/jdbc",
|
|
1337
1364
|
output: gen.output,
|
|
1338
1365
|
config: gen.config,
|
|
1339
1366
|
projectName: gen.projectName
|
|
1340
1367
|
}, this.javaGenerator, name, q, tables, "duckdb");
|
|
1368
|
+
const owners = /* @__PURE__ */ new Set();
|
|
1369
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1370
|
+
for (const q of queries) {
|
|
1371
|
+
if (q.skipGenerateFunction) continue;
|
|
1372
|
+
if (!q.isQuery || q.isOne) continue;
|
|
1373
|
+
if (q.columns.length === 0) continue;
|
|
1374
|
+
const rowTypeName = this.rowType(q);
|
|
1375
|
+
if (seen.has(rowTypeName)) continue;
|
|
1376
|
+
seen.add(rowTypeName);
|
|
1377
|
+
owners.add(q.id);
|
|
1378
|
+
}
|
|
1379
|
+
Handlebars.registerHelper("shouldDeclareType", (queryHelper) => owners.has(queryHelper.id));
|
|
1341
1380
|
}
|
|
1342
1381
|
isCompatibleWith(engine) {
|
|
1343
1382
|
return engine === "duckdb";
|
|
@@ -1399,7 +1438,7 @@ var JavaDuckDBArrowGenerator = class extends BaseGenerator {
|
|
|
1399
1438
|
}
|
|
1400
1439
|
rowType(query) {
|
|
1401
1440
|
if (query.isOne) return this.javaGenerator.rowType(query);
|
|
1402
|
-
return this.getClassName(`${query.id}_Result`);
|
|
1441
|
+
return this.getClassName(query.resultTypeName ?? `${query.id}_Result`);
|
|
1403
1442
|
}
|
|
1404
1443
|
};
|
|
1405
1444
|
//#endregion
|
|
@@ -1459,6 +1498,19 @@ var PythonGenerator = class extends BaseGenerator {
|
|
|
1459
1498
|
if (value.includes("\n") || value.includes("'") || value.includes("\"")) return `"""\\\n${value}"""`;
|
|
1460
1499
|
return `"${value}"`;
|
|
1461
1500
|
});
|
|
1501
|
+
Handlebars.registerHelper("sqlExpr", (queryHelper) => {
|
|
1502
|
+
const parts = queryHelper.sqlQueryParts;
|
|
1503
|
+
if (!parts.some((p) => typeof p !== "string" && p.name.startsWith("sources_"))) {
|
|
1504
|
+
const value = queryHelper.sqlQuery;
|
|
1505
|
+
if (value.includes("\n") || value.includes("'") || value.includes("\"")) return `"""\\\n${value}"""`;
|
|
1506
|
+
return `"${value}"`;
|
|
1507
|
+
}
|
|
1508
|
+
let out = "f\"\"\"";
|
|
1509
|
+
for (const part of parts) if (typeof part === "string") out += part.replace(/\\/g, "\\\\").replace(/\{/g, "{{").replace(/\}/g, "}}");
|
|
1510
|
+
else if (part.name.startsWith("sources_")) out += ` '{${part.name}}'`;
|
|
1511
|
+
else out += "?";
|
|
1512
|
+
return `${out}"""`;
|
|
1513
|
+
});
|
|
1462
1514
|
Handlebars.registerHelper("pyType", (column) => {
|
|
1463
1515
|
return this.getPyType(column);
|
|
1464
1516
|
});
|
|
@@ -1583,6 +1635,7 @@ var TsGenerator = class extends BaseGenerator {
|
|
|
1583
1635
|
async beforeGenerate(_projectDir, gen, _queries, _tables) {
|
|
1584
1636
|
if (this.typeMapper instanceof TypeScriptTypeMapper) this.typeMapper.safeIntegers = !!gen.config?.safeIntegers;
|
|
1585
1637
|
Handlebars.registerHelper("quote", (value) => this.quote(value));
|
|
1638
|
+
Handlebars.registerHelper("sqlExpr", (queryHelper) => this.sqlExpr(queryHelper));
|
|
1586
1639
|
Handlebars.registerHelper("appendMethod", (column) => {
|
|
1587
1640
|
if (column.type instanceof ListType) return "List";
|
|
1588
1641
|
const typeStr = column.type?.toString().toUpperCase() || "";
|
|
@@ -1735,6 +1788,22 @@ var TsGenerator = class extends BaseGenerator {
|
|
|
1735
1788
|
quote(value) {
|
|
1736
1789
|
return value.includes("\n") || value.includes("'") ? `\`${value}\`` : `'${value}'`;
|
|
1737
1790
|
}
|
|
1791
|
+
/**
|
|
1792
|
+
* Build the SQL expression for a query. Without file `sources`, this is the
|
|
1793
|
+
* plain quoted SQL string (unchanged). When a `${sources_x}` reference is
|
|
1794
|
+
* present it is interpolated into a template literal as a runtime argument
|
|
1795
|
+
* (the value is supplied by the caller, not bound), mirroring the Java
|
|
1796
|
+
* generator — `ATTACH '` + arg + `'` style.
|
|
1797
|
+
*/
|
|
1798
|
+
sqlExpr(queryHelper) {
|
|
1799
|
+
const parts = queryHelper.sqlQueryParts;
|
|
1800
|
+
if (!parts.some((p) => typeof p !== "string" && p.name.startsWith("sources_"))) return this.quote(queryHelper.sqlQuery);
|
|
1801
|
+
let out = "`";
|
|
1802
|
+
for (const part of parts) if (typeof part === "string") out += part.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
1803
|
+
else if (part.name.startsWith("sources_")) out += ` '\${${part.name}}'`;
|
|
1804
|
+
else out += "?";
|
|
1805
|
+
return `${out}\``;
|
|
1806
|
+
}
|
|
1738
1807
|
};
|
|
1739
1808
|
//#endregion
|
|
1740
1809
|
//#region src/generators/typescript-duckdb-generator.ts
|
|
@@ -1841,6 +1910,57 @@ function getGenerator(generator) {
|
|
|
1841
1910
|
}
|
|
1842
1911
|
}
|
|
1843
1912
|
//#endregion
|
|
1913
|
+
//#region src/sources.ts
|
|
1914
|
+
/**
|
|
1915
|
+
* For each `type: postgres` source: start a throwaway Postgres testcontainer,
|
|
1916
|
+
* run the source's schema (the BASELINE blocks tagged `:source=<name>`) natively
|
|
1917
|
+
* against it so introspection sees true Postgres types, and return the connection
|
|
1918
|
+
* info to ATTACH into DuckDB.
|
|
1919
|
+
*
|
|
1920
|
+
* The synthesized ATTACH (done by the DuckDB adapter) and these schema blocks are
|
|
1921
|
+
* not emitted into generated code — at runtime the application attaches the real
|
|
1922
|
+
* production database under the same alias.
|
|
1923
|
+
*/
|
|
1924
|
+
async function preparePostgresSources(sources, queries, reporter) {
|
|
1925
|
+
const containers = [];
|
|
1926
|
+
const attachments = [];
|
|
1927
|
+
const teardown = async () => {
|
|
1928
|
+
for (const container of containers) try {
|
|
1929
|
+
await container.stop();
|
|
1930
|
+
} catch {}
|
|
1931
|
+
};
|
|
1932
|
+
try {
|
|
1933
|
+
for (const source of sources) {
|
|
1934
|
+
reporter?.onContainerStarting?.();
|
|
1935
|
+
const container = await new PostgreSqlContainer(source.image).withDatabase("sqg-db").withUsername("sqg").withPassword("secret").start();
|
|
1936
|
+
containers.push(container);
|
|
1937
|
+
const connectionUri = container.getConnectionUri();
|
|
1938
|
+
reporter?.onContainerStarted?.(connectionUri);
|
|
1939
|
+
const schemaBlocks = queries.filter((q) => q.isBaseline && q.sourceTarget === source.name);
|
|
1940
|
+
const client = new Client({ connectionString: connectionUri });
|
|
1941
|
+
await client.connect();
|
|
1942
|
+
try {
|
|
1943
|
+
for (const block of schemaBlocks) await client.query(block.rawQuery);
|
|
1944
|
+
} catch (e) {
|
|
1945
|
+
throw new DatabaseError(`Failed to apply schema for postgres source '${source.name}': ${e.message}`, "postgres", `Check that the BASELINE blocks tagged ':source=${source.name}' are valid PostgreSQL.`);
|
|
1946
|
+
} finally {
|
|
1947
|
+
await client.end();
|
|
1948
|
+
}
|
|
1949
|
+
attachments.push({
|
|
1950
|
+
alias: source.name,
|
|
1951
|
+
connectionUri
|
|
1952
|
+
});
|
|
1953
|
+
}
|
|
1954
|
+
} catch (e) {
|
|
1955
|
+
await teardown();
|
|
1956
|
+
throw e;
|
|
1957
|
+
}
|
|
1958
|
+
return {
|
|
1959
|
+
attachments,
|
|
1960
|
+
teardown
|
|
1961
|
+
};
|
|
1962
|
+
}
|
|
1963
|
+
//#endregion
|
|
1844
1964
|
//#region src/sqltool.ts
|
|
1845
1965
|
const GENERATED_FILE_COMMENT = "This file is generated by SQG (https://sqg.dev). Do not edit manually.";
|
|
1846
1966
|
const configSchema = z.object({ result: z.record(z.string(), z.string()).optional() });
|
|
@@ -2017,18 +2137,23 @@ var TableHelper = class {
|
|
|
2017
2137
|
return this.generator.typeMapper;
|
|
2018
2138
|
}
|
|
2019
2139
|
};
|
|
2020
|
-
function generateSourceFile(name, queries, tables, templatePath, generator, engine, projectName, config) {
|
|
2140
|
+
function generateSourceFile(name, queries, tables, templatePath, generator, engine, projectName, config, postgresSourceNames = []) {
|
|
2021
2141
|
const templateSrc = readFileSync(templatePath, "utf-8");
|
|
2022
2142
|
const template = Handlebars.compile(templateSrc);
|
|
2023
2143
|
Handlebars.registerHelper("mapType", (column) => generator.mapType(column));
|
|
2024
2144
|
Handlebars.registerHelper("plusOne", (value) => value + 1);
|
|
2025
2145
|
const migrations = queries.filter((q) => q.isMigrate).map((q) => new SqlQueryHelper(q, generator, generator.getStatement(q)));
|
|
2026
2146
|
const tableHelpers = generator.supportsAppenders(engine) ? tables.filter((t) => !t.skipGenerateFunction).map((t) => new TableHelper(t, generator)) : [];
|
|
2147
|
+
const attachers = engine === "duckdb" ? postgresSourceNames.map((sourceName) => ({
|
|
2148
|
+
alias: sourceName,
|
|
2149
|
+
functionName: generator.getFunctionName(`attach_${sourceName}`)
|
|
2150
|
+
})) : [];
|
|
2027
2151
|
return template({
|
|
2028
2152
|
generatedComment: GENERATED_FILE_COMMENT,
|
|
2029
2153
|
migrations,
|
|
2030
2154
|
queries: queries.map((q) => new SqlQueryHelper(q, generator, generator.getStatement(q))),
|
|
2031
2155
|
tables: tableHelpers,
|
|
2156
|
+
attachers,
|
|
2032
2157
|
className: generator.getClassName(name),
|
|
2033
2158
|
projectName,
|
|
2034
2159
|
config
|
|
@@ -2056,9 +2181,11 @@ const ProjectSchema = z.object({
|
|
|
2056
2181
|
})).min(1, "At least one generator is required").describe("Code generators to run")
|
|
2057
2182
|
})).min(1, "At least one SQL configuration is required").describe("SQL file configurations"),
|
|
2058
2183
|
sources: z.array(z.object({
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2184
|
+
type: z.enum(["file", "postgres"]).optional().describe("Source type: 'file' (default) or 'postgres'"),
|
|
2185
|
+
path: z.string().optional().describe("Path to source file for file sources (supports $HOME)"),
|
|
2186
|
+
name: z.string().optional().describe("Variable name (file sources) or DuckDB attach alias (postgres sources)"),
|
|
2187
|
+
image: z.string().optional().describe("Docker image for postgres sources (default: postgres:16-alpine)")
|
|
2188
|
+
}).refine((s) => s.type === "postgres" ? !!s.name : !!s.path, { message: "file sources require 'path'; postgres sources require 'name'" })).optional().describe("External sources: files inlined as variables, or postgres databases attached for introspection")
|
|
2062
2189
|
});
|
|
2063
2190
|
var ExtraVariable = class {
|
|
2064
2191
|
constructor(name, value) {
|
|
@@ -2067,7 +2194,7 @@ var ExtraVariable = class {
|
|
|
2067
2194
|
}
|
|
2068
2195
|
};
|
|
2069
2196
|
function createExtraVariables(sources, suppressLogging = false) {
|
|
2070
|
-
return sources.map((source) => {
|
|
2197
|
+
return sources.filter((source) => source.type !== "postgres").map((source) => {
|
|
2071
2198
|
const path = source.path;
|
|
2072
2199
|
const resolvedPath = path.replace("$HOME", homedir());
|
|
2073
2200
|
const varName = `sources_${(source.name ?? basename(path, extname(resolvedPath))).replace(/\s+/g, "_")}`;
|
|
@@ -2075,6 +2202,13 @@ function createExtraVariables(sources, suppressLogging = false) {
|
|
|
2075
2202
|
return new ExtraVariable(varName, `'${resolvedPath}'`);
|
|
2076
2203
|
});
|
|
2077
2204
|
}
|
|
2205
|
+
/** Postgres sources resolved from the project config (with image defaulted). */
|
|
2206
|
+
function getPostgresSources(sources) {
|
|
2207
|
+
return sources.filter((source) => source.type === "postgres").map((source) => ({
|
|
2208
|
+
name: source.name,
|
|
2209
|
+
image: source.image ?? "postgres:16-alpine"
|
|
2210
|
+
}));
|
|
2211
|
+
}
|
|
2078
2212
|
function buildProjectFromCliOptions(options) {
|
|
2079
2213
|
if (!isValidGenerator(options.generator)) {
|
|
2080
2214
|
const similar = findSimilarGenerators(options.generator);
|
|
@@ -2151,6 +2285,106 @@ function getOutputPath(projectDir, sqlFileName, gen, generator) {
|
|
|
2151
2285
|
mkdirSync(dirname(outputPath), { recursive: true });
|
|
2152
2286
|
return outputPath;
|
|
2153
2287
|
}
|
|
2288
|
+
function assignResultTypeNames(queries, tables, projectDir = process.cwd()) {
|
|
2289
|
+
const hints = [];
|
|
2290
|
+
const tablesByName = /* @__PURE__ */ new Map();
|
|
2291
|
+
for (const t of tables) tablesByName.set(t.tableName, t);
|
|
2292
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2293
|
+
const eligible = queries.filter((q) => q.isQuery && !q.isPluck && !q.skipGenerateFunction && q.columns.length > 0);
|
|
2294
|
+
for (const q of eligible) {
|
|
2295
|
+
const cols = effectiveColumns(q);
|
|
2296
|
+
const key = JSON.stringify(cols.map((c) => ({
|
|
2297
|
+
n: c.name,
|
|
2298
|
+
t: c.type.toString(),
|
|
2299
|
+
nu: c.nullable,
|
|
2300
|
+
s: c.sourceTable ?? null
|
|
2301
|
+
})));
|
|
2302
|
+
let g = groups.get(key);
|
|
2303
|
+
if (!g) {
|
|
2304
|
+
g = { queries: [] };
|
|
2305
|
+
groups.set(key, g);
|
|
2306
|
+
}
|
|
2307
|
+
g.queries.push(q);
|
|
2308
|
+
}
|
|
2309
|
+
const recordShape = (cols) => JSON.stringify(cols.map((c) => ({
|
|
2310
|
+
n: c.name,
|
|
2311
|
+
t: c.type.toString(),
|
|
2312
|
+
nu: c.nullable
|
|
2313
|
+
})));
|
|
2314
|
+
const claims = /* @__PURE__ */ new Map();
|
|
2315
|
+
for (const t of tables) {
|
|
2316
|
+
if (!t.hasAppender) continue;
|
|
2317
|
+
const cols = t.columns.filter((c) => !c.generated);
|
|
2318
|
+
claims.set(pascalCase(`${t.id}_row`), {
|
|
2319
|
+
shape: recordShape(cols),
|
|
2320
|
+
offenderId: t.id,
|
|
2321
|
+
source: `TABLE ${t.tableName}`
|
|
2322
|
+
});
|
|
2323
|
+
}
|
|
2324
|
+
const claim = (name, shape, representative, source) => {
|
|
2325
|
+
const canonical = pascalCase(name);
|
|
2326
|
+
const existing = claims.get(canonical);
|
|
2327
|
+
if (existing && existing.shape !== shape) throw SqgError.inQuery(`Result type '${name}' would be emitted with two incompatible shapes: ${source} ('${representative.id}') vs ${existing.source} ('${existing.offenderId}')`, "VALIDATION_ERROR", representative.id, representative.filename, { suggestion: "Use a different `:result=` name for one of the queries, or change the queries so their result columns match" });
|
|
2328
|
+
if (!existing) claims.set(canonical, {
|
|
2329
|
+
shape,
|
|
2330
|
+
offenderId: representative.id,
|
|
2331
|
+
source
|
|
2332
|
+
});
|
|
2333
|
+
};
|
|
2334
|
+
for (const group of groups.values()) {
|
|
2335
|
+
const overrides = group.queries.map((q) => q.resultTypeOverride).filter((v) => !!v);
|
|
2336
|
+
const uniqueOverrides = new Set(overrides);
|
|
2337
|
+
if (uniqueOverrides.size > 1) {
|
|
2338
|
+
const offenders = group.queries.filter((q) => q.resultTypeOverride);
|
|
2339
|
+
throw SqgError.inQuery(`Conflicting ':result=' names for queries with identical result shapes: ${offenders.map((q) => `'${q.id}' uses '${q.resultTypeOverride}'`).join(", ")}`, "VALIDATION_ERROR", offenders[0].id, offenders[0].filename, { suggestion: "Pick a single name for this row type or change the queries so their shapes differ" });
|
|
2340
|
+
}
|
|
2341
|
+
const shape = recordShape(effectiveColumns(group.queries[0]));
|
|
2342
|
+
if (uniqueOverrides.size === 1) {
|
|
2343
|
+
const resolved = [...uniqueOverrides][0];
|
|
2344
|
+
claim(resolved, shape, group.queries.find((q) => q.resultTypeOverride === resolved), `:result=${resolved}`);
|
|
2345
|
+
for (const q of group.queries) q.resultTypeName = resolved;
|
|
2346
|
+
continue;
|
|
2347
|
+
}
|
|
2348
|
+
const fullMatch = tryFullTableMatch(group.queries[0], tablesByName);
|
|
2349
|
+
if (fullMatch) {
|
|
2350
|
+
claim(fullMatch, shape, group.queries[0], "full-table match");
|
|
2351
|
+
for (const q of group.queries) q.resultTypeName = fullMatch;
|
|
2352
|
+
continue;
|
|
2353
|
+
}
|
|
2354
|
+
if (group.queries.length >= 2) {
|
|
2355
|
+
const list = group.queries.map((q) => `'${q.id}' (${displayPath(q.filename, projectDir)}:${q.line})`).join(", ");
|
|
2356
|
+
hints.push({
|
|
2357
|
+
queryIds: group.queries.map((q) => q.id),
|
|
2358
|
+
message: `${group.queries.length} queries return the same row shape: ${list}. Add \`:result=Name\` to any one of them to share a single row type.`
|
|
2359
|
+
});
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
return hints;
|
|
2363
|
+
}
|
|
2364
|
+
/** Show paths relative to the project root when it's inside the tree; otherwise fall back to absolute. */
|
|
2365
|
+
function displayPath(fullPath, projectDir) {
|
|
2366
|
+
const rel = relative(projectDir, fullPath);
|
|
2367
|
+
return rel && !rel.startsWith("..") ? rel : fullPath;
|
|
2368
|
+
}
|
|
2369
|
+
/** Resolve config-overridden column info, mirroring what validateQueries does for allColumns. */
|
|
2370
|
+
function effectiveColumns(query) {
|
|
2371
|
+
return query.columns.map((col) => query.config?.getColumnInfo(col.name) ?? col);
|
|
2372
|
+
}
|
|
2373
|
+
function tryFullTableMatch(query, tablesByName) {
|
|
2374
|
+
const cols = effectiveColumns(query);
|
|
2375
|
+
const firstSource = cols[0]?.sourceTable;
|
|
2376
|
+
if (!firstSource) return void 0;
|
|
2377
|
+
if (!cols.every((c) => c.sourceTable === firstSource)) return void 0;
|
|
2378
|
+
const table = tablesByName.get(firstSource);
|
|
2379
|
+
if (!table) return void 0;
|
|
2380
|
+
if (table.columns.some((c) => c.generated)) return void 0;
|
|
2381
|
+
if (table.columns.length !== cols.length) return void 0;
|
|
2382
|
+
for (let i = 0; i < cols.length; i++) {
|
|
2383
|
+
if (cols[i].name !== table.columns[i].name) return void 0;
|
|
2384
|
+
if (cols[i].type.toString() !== table.columns[i].type.toString()) return void 0;
|
|
2385
|
+
}
|
|
2386
|
+
return `${table.id}_row`;
|
|
2387
|
+
}
|
|
2154
2388
|
function validateQueries(queries) {
|
|
2155
2389
|
for (const query of queries) {
|
|
2156
2390
|
if (query.isQuery && query.isPluck && query.columns.length !== 1) throw SqgError.inQuery(`':pluck' modifier requires exactly 1 column, but query has ${query.columns.length} columns`, "VALIDATION_ERROR", query.id, query.filename, { suggestion: query.columns.length === 0 ? "Ensure the query returns at least one column" : `Remove ':pluck' or select only one column. Current columns: ${query.columns.map((c) => c.name).join(", ")}` });
|
|
@@ -2166,14 +2400,14 @@ function validateQueries(queries) {
|
|
|
2166
2400
|
};
|
|
2167
2401
|
}
|
|
2168
2402
|
}
|
|
2169
|
-
async function writeGeneratedFile(projectDir, gen, generator, file, queries, tables, engine, writeToStdout = false) {
|
|
2403
|
+
async function writeGeneratedFile(projectDir, gen, generator, file, queries, tables, engine, writeToStdout = false, postgresSourceNames = []) {
|
|
2170
2404
|
await generator.beforeGenerate(projectDir, gen, queries, tables);
|
|
2171
2405
|
const templatePath = join(dirname(new URL(import.meta.url).pathname), gen.template ?? generator.template);
|
|
2172
2406
|
const name = gen.name ?? basename(file, extname(file));
|
|
2173
2407
|
const sourceFile = generateSourceFile(name, queries, tables, templatePath, generator, engine, gen.projectName ?? name, {
|
|
2174
2408
|
migrations: true,
|
|
2175
2409
|
...gen.config
|
|
2176
|
-
});
|
|
2410
|
+
}, postgresSourceNames);
|
|
2177
2411
|
if (writeToStdout) {
|
|
2178
2412
|
process.stdout.write(sourceFile);
|
|
2179
2413
|
if (!sourceFile.endsWith("\n")) process.stdout.write("\n");
|
|
@@ -2274,6 +2508,11 @@ async function processProjectFromConfig(project, projectDir, writeToStdout = fal
|
|
|
2274
2508
|
const results = [];
|
|
2275
2509
|
try {
|
|
2276
2510
|
const extraVariables = createExtraVariables(project.sources ?? [], writeToStdout);
|
|
2511
|
+
const pgSources = getPostgresSources(project.sources ?? []);
|
|
2512
|
+
const pgSourceNames = new Set(pgSources.map((s) => s.name));
|
|
2513
|
+
if (pgSources.length > 0) {
|
|
2514
|
+
if (!project.sql.some((sql) => sql.gen.some((gen) => getGeneratorEngine(gen.generator) === "duckdb"))) throw new SqgError("Postgres sources require a DuckDB generator (they are attached into DuckDB for introspection)", "CONFIG_VALIDATION_ERROR", "Add a 'typescript/duckdb' or 'java/duckdb' generator, or remove the postgres sources");
|
|
2515
|
+
}
|
|
2277
2516
|
const files = [];
|
|
2278
2517
|
for (const sql of project.sql) {
|
|
2279
2518
|
const gensByEngine = /* @__PURE__ */ new Map();
|
|
@@ -2291,6 +2530,7 @@ async function processProjectFromConfig(project, projectDir, writeToStdout = fal
|
|
|
2291
2530
|
const parseResult = parseSQLQueries(fullPath, extraVariables);
|
|
2292
2531
|
queries = parseResult.queries;
|
|
2293
2532
|
tables = parseResult.tables;
|
|
2533
|
+
for (const q of queries) if (q.sourceTarget && !pgSourceNames.has(q.sourceTarget)) throw SqgError.inQuery(`BASELINE block targets unknown postgres source '${q.sourceTarget}'`, "CONFIG_VALIDATION_ERROR", q.id, sqlFile, { suggestion: `Declare a source with 'type: postgres, name: ${q.sourceTarget}' in sqg.yaml` });
|
|
2294
2534
|
} catch (e) {
|
|
2295
2535
|
if (e instanceof SqgError) throw e;
|
|
2296
2536
|
throw SqgError.inFile(`Failed to parse SQL file: ${e.message}`, "SQL_PARSE_ERROR", sqlFile, { suggestion: "Check SQL syntax and annotation format" });
|
|
@@ -2299,15 +2539,25 @@ async function processProjectFromConfig(project, projectDir, writeToStdout = fal
|
|
|
2299
2539
|
const executableQueries = queries.filter((q) => !q.skipGenerateFunction);
|
|
2300
2540
|
const reporterAny = reporter;
|
|
2301
2541
|
if (reporterAny?.setQueryTotal) reporterAny.setQueryTotal(executableQueries.length);
|
|
2542
|
+
let preparedSources;
|
|
2302
2543
|
try {
|
|
2303
2544
|
const dbEngine = getDatabaseEngine(engine);
|
|
2545
|
+
let attachments;
|
|
2546
|
+
if (engine === "duckdb" && pgSources.length > 0) {
|
|
2547
|
+
ui?.startPhase("Starting postgres source containers...");
|
|
2548
|
+
preparedSources = await preparePostgresSources(pgSources, queries, reporter);
|
|
2549
|
+
attachments = preparedSources.attachments;
|
|
2550
|
+
}
|
|
2304
2551
|
ui?.startPhase(`Initializing ${engine} database...`);
|
|
2305
|
-
await dbEngine.initializeDatabase(queries, reporter);
|
|
2552
|
+
await dbEngine.initializeDatabase(queries, reporter, { attachments });
|
|
2306
2553
|
ui?.startPhase(`Introspecting ${executableQueries.length} queries...`);
|
|
2307
2554
|
await dbEngine.executeQueries(queries, reporter);
|
|
2308
2555
|
if (tables.length > 0) await dbEngine.introspectTables(tables, reporter);
|
|
2309
2556
|
ui?.succeedPhase(`Introspected ${executableQueries.length} queries`);
|
|
2310
2557
|
validateQueries(queries);
|
|
2558
|
+
const hints = assignResultTypeNames(queries, tables, projectDir);
|
|
2559
|
+
for (const hint of hints) if (ui) ui.hint(hint.message);
|
|
2560
|
+
else consola.info(hint.message);
|
|
2311
2561
|
await dbEngine.close();
|
|
2312
2562
|
} catch (e) {
|
|
2313
2563
|
ui?.failPhase(`Failed to process ${sqlFile}`);
|
|
@@ -2316,6 +2566,8 @@ async function processProjectFromConfig(project, projectDir, writeToStdout = fal
|
|
|
2316
2566
|
file: sqlFile,
|
|
2317
2567
|
engine
|
|
2318
2568
|
});
|
|
2569
|
+
} finally {
|
|
2570
|
+
if (preparedSources) await preparedSources.teardown();
|
|
2319
2571
|
}
|
|
2320
2572
|
ui?.startPhase("Generating code...");
|
|
2321
2573
|
const genStart = performance.now();
|
|
@@ -2324,7 +2576,7 @@ async function processProjectFromConfig(project, projectDir, writeToStdout = fal
|
|
|
2324
2576
|
const outputPath = await writeGeneratedFile(projectDir, {
|
|
2325
2577
|
...gen,
|
|
2326
2578
|
projectName: project.name
|
|
2327
|
-
}, generator, sqlFile, queries, tables, engine, writeToStdout);
|
|
2579
|
+
}, generator, sqlFile, queries, tables, engine, writeToStdout, pgSources.map((s) => s.name));
|
|
2328
2580
|
if (outputPath !== null) {
|
|
2329
2581
|
files.push(outputPath);
|
|
2330
2582
|
results.push({
|
|
@@ -2395,6 +2647,17 @@ var SQLQuery = class {
|
|
|
2395
2647
|
allColumns;
|
|
2396
2648
|
/** Database-reported parameter types (variable name → SQL type), set by database adapters */
|
|
2397
2649
|
parameterTypes;
|
|
2650
|
+
/** Explicit row-type name from `:result=Name` modifier, when present. */
|
|
2651
|
+
resultTypeOverride;
|
|
2652
|
+
/**
|
|
2653
|
+
* Name of the postgres `source` this block targets, from a `:source=name`
|
|
2654
|
+
* modifier on a BASELINE block. Such blocks define the schema of an external
|
|
2655
|
+
* postgres source and run natively against that source's container during
|
|
2656
|
+
* generation (not against the DuckDB introspection connection).
|
|
2657
|
+
*/
|
|
2658
|
+
sourceTarget;
|
|
2659
|
+
/** Final row-type identifier assigned by assignResultTypeNames (after introspection). */
|
|
2660
|
+
resultTypeName;
|
|
2398
2661
|
constructor(filename, id, rawQuery, queryAnonymous, queryNamed, queryPositional, type, isOne, isPluck, isBatch, variables, config, line) {
|
|
2399
2662
|
this.filename = filename;
|
|
2400
2663
|
this.id = id;
|
|
@@ -2508,6 +2771,9 @@ function parseSQLQueries(filePath, extraVariables) {
|
|
|
2508
2771
|
const isOne = modifiers.includes(":one");
|
|
2509
2772
|
const isPluck = modifiers.includes(":pluck");
|
|
2510
2773
|
const isBatch = modifiers.includes(":batch");
|
|
2774
|
+
const resultTypeOverride = modifiers.find((m) => m.startsWith(":result="))?.slice(8);
|
|
2775
|
+
const sourceTarget = modifiers.find((m) => m.startsWith(":source="))?.slice(8);
|
|
2776
|
+
if (sourceTarget && queryType !== "BASELINE") throw SqgError.inQuery(`The ':source=' modifier is only valid on BASELINE blocks, not ${queryType}`, "SQL_PARSE_ERROR", name, filePath, { suggestion: "Move the schema for a postgres source into a '-- BASELINE <name> :source=<source>' block" });
|
|
2511
2777
|
let configStr = getStr("Config", true);
|
|
2512
2778
|
if (configStr?.endsWith("*/")) configStr = configStr.slice(0, -2);
|
|
2513
2779
|
let config = null;
|
|
@@ -2662,6 +2928,8 @@ function parseSQLQueries(filePath, extraVariables) {
|
|
|
2662
2928
|
config
|
|
2663
2929
|
});
|
|
2664
2930
|
const query = new SQLQuery(filePath, name, sqlContentStr, sql.toSqlWithAnonymousPlaceholders(), sql.toSqlWithNamedPlaceholders(), sql.toSqlWithPositionalPlaceholders(), queryType, isOne, isPluck, isBatch, variables, config, annotationLine);
|
|
2931
|
+
query.resultTypeOverride = resultTypeOverride;
|
|
2932
|
+
query.sourceTarget = sourceTarget;
|
|
2665
2933
|
checkDuplicate(queryType, name);
|
|
2666
2934
|
queries.push(query);
|
|
2667
2935
|
consola.debug(`Added query: ${name} (${queryType})`);
|
|
@@ -2678,7 +2946,7 @@ function parseSQLQueries(filePath, extraVariables) {
|
|
|
2678
2946
|
//#endregion
|
|
2679
2947
|
//#region src/db/types.ts
|
|
2680
2948
|
async function initializeDatabase(queries, execQueries, reporter) {
|
|
2681
|
-
const baselineQueries = queries.filter((q) => q.isBaseline);
|
|
2949
|
+
const baselineQueries = queries.filter((q) => q.isBaseline && !q.sourceTarget);
|
|
2682
2950
|
for (const query of baselineQueries) try {
|
|
2683
2951
|
await execQueries(query);
|
|
2684
2952
|
} catch (error) {
|
|
@@ -2734,9 +3002,17 @@ function convertType(type) {
|
|
|
2734
3002
|
const duckdb = new class {
|
|
2735
3003
|
db;
|
|
2736
3004
|
connection;
|
|
2737
|
-
async initializeDatabase(queries, reporter) {
|
|
3005
|
+
async initializeDatabase(queries, reporter, options) {
|
|
2738
3006
|
this.db = await DuckDBInstance.create(":memory:");
|
|
2739
3007
|
this.connection = await this.db.connect();
|
|
3008
|
+
for (const attachment of options?.attachments ?? []) {
|
|
3009
|
+
const sql = `ATTACH '${attachment.connectionUri}' AS ${attachment.alias} (TYPE postgres)`;
|
|
3010
|
+
try {
|
|
3011
|
+
await this.connection.run(sql);
|
|
3012
|
+
} catch (e) {
|
|
3013
|
+
throw new DatabaseError(`Failed to attach source '${attachment.alias}': ${e.message}`, "duckdb", "Check that the postgres source container started and is reachable.");
|
|
3014
|
+
}
|
|
3015
|
+
}
|
|
2740
3016
|
await initializeDatabase(queries, async (query) => {
|
|
2741
3017
|
try {
|
|
2742
3018
|
await this.connection.run(query.rawQuery);
|
|
@@ -2946,6 +3222,7 @@ const postgres = new class {
|
|
|
2946
3222
|
mode;
|
|
2947
3223
|
dynamicTypeCache = /* @__PURE__ */ new Map();
|
|
2948
3224
|
enumTypeCache = /* @__PURE__ */ new Map();
|
|
3225
|
+
tableOidCache = /* @__PURE__ */ new Map();
|
|
2949
3226
|
async startContainer(reporter) {
|
|
2950
3227
|
reporter?.onContainerStarting?.();
|
|
2951
3228
|
const container = await new PostgreSqlContainer("postgres:16-alpine").withDatabase("sqg-db").withUsername("sqg").withPassword("secret").start();
|
|
@@ -2996,10 +3273,17 @@ const postgres = new class {
|
|
|
2996
3273
|
if (enumType) return enumType;
|
|
2997
3274
|
return this.getTypeName(dataTypeID);
|
|
2998
3275
|
}
|
|
3276
|
+
async resolveTableOids(db, oids) {
|
|
3277
|
+
const missing = oids.filter((oid) => !this.tableOidCache.has(oid));
|
|
3278
|
+
if (missing.length === 0) return;
|
|
3279
|
+
const res = await db.query("SELECT oid::int AS oid, relname FROM pg_class WHERE oid = ANY($1)", [missing]);
|
|
3280
|
+
for (const row of res.rows) this.tableOidCache.set(Number(row.oid), row.relname);
|
|
3281
|
+
}
|
|
2999
3282
|
async initializeDatabase(queries, reporter) {
|
|
3000
3283
|
const externalUrl = process.env.SQG_POSTGRES_URL;
|
|
3001
3284
|
this.dynamicTypeCache = /* @__PURE__ */ new Map();
|
|
3002
3285
|
this.enumTypeCache = /* @__PURE__ */ new Map();
|
|
3286
|
+
this.tableOidCache = /* @__PURE__ */ new Map();
|
|
3003
3287
|
if (externalUrl) this.mode = new ExternalDbMode(externalUrl);
|
|
3004
3288
|
else {
|
|
3005
3289
|
const { connectionUri, container } = await this.startContainer(reporter);
|
|
@@ -3065,11 +3349,18 @@ const postgres = new class {
|
|
|
3065
3349
|
});
|
|
3066
3350
|
consola.debug("Columns:", columnNames);
|
|
3067
3351
|
consola.debug("Types:", columnTypes.map((t) => t.toString()));
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3352
|
+
const tableOids = Array.from(new Set(result.fields.map((f) => f.tableID).filter((oid) => oid && oid > 0)));
|
|
3353
|
+
await this.resolveTableOids(db, tableOids);
|
|
3354
|
+
query.columns = columnNames.map((name, index) => {
|
|
3355
|
+
const field = result.fields[index];
|
|
3356
|
+
const sourceTable = field.tableID && field.tableID > 0 ? this.tableOidCache.get(field.tableID) : void 0;
|
|
3357
|
+
return {
|
|
3358
|
+
name,
|
|
3359
|
+
type: columnTypes[index],
|
|
3360
|
+
nullable: true,
|
|
3361
|
+
sourceTable
|
|
3362
|
+
};
|
|
3363
|
+
});
|
|
3073
3364
|
}
|
|
3074
3365
|
if (query.isQuery) {
|
|
3075
3366
|
if (query.isOne) return result.rows[0] || null;
|
|
@@ -3107,6 +3398,7 @@ const postgres = new class {
|
|
|
3107
3398
|
await this.mode.close(this.db);
|
|
3108
3399
|
this.dynamicTypeCache = /* @__PURE__ */ new Map();
|
|
3109
3400
|
this.enumTypeCache = /* @__PURE__ */ new Map();
|
|
3401
|
+
this.tableOidCache = /* @__PURE__ */ new Map();
|
|
3110
3402
|
}
|
|
3111
3403
|
}();
|
|
3112
3404
|
//#endregion
|
|
@@ -3217,7 +3509,8 @@ const sqlite = new class {
|
|
|
3217
3509
|
return {
|
|
3218
3510
|
name: col.name,
|
|
3219
3511
|
type: col.type || probed.type || "unknown",
|
|
3220
|
-
nullable: col.table ? colInfo?.pk === 0 && colInfo?.notnull === 0 : probed.nullable
|
|
3512
|
+
nullable: col.table ? colInfo?.pk === 0 && colInfo?.notnull === 0 : probed.nullable,
|
|
3513
|
+
sourceTable: col.table ?? void 0
|
|
3221
3514
|
};
|
|
3222
3515
|
});
|
|
3223
3516
|
}
|