diff --git a/video/edgedb-day-1.0/subtitles/6.bcc b/video/edgedb-day-1.0/subtitles/6.bcc index f8a125a69221d611461d50a46bd0df6355ee7ea3..fa2aa8d456d60d373369ecdfd2f6ac45e8dc9a3a 100644 --- a/video/edgedb-day-1.0/subtitles/6.bcc +++ b/video/edgedb-day-1.0/subtitles/6.bcc @@ -1 +1,1942 @@ -{"font_size":0.4,"font_color":"#FFFFFF","background_alpha":0.5,"background_color":"#9C27B0","Stroke":"none","body":[{"from":6.781775,"to":8.45,"location":2,"content":"All right."},{"from":9.434425,"to":15.440425,"location":2,"content":"Hi everyone, um and like Colin said, I'm a recovering academic, I've got my uh"},{"from":15.440425,"to":18.81879899900057,"location":2,"content":"clicker from my thesis defense which is hopefully good luck."},{"from":18.893968093000378,"to":22.24722500000018,"location":2,"content":"I'm going to be talking about compiling EdgeQL, "},{"from":22.24722500000018,"to":25.575549999999996,"location":2,"content":"which is subtitled uh, Standing on the shoulders of giants. "},{"from":25.675650000000363,"to":29.62960000000018,"location":2,"content":"Because uh we think we have with EdgeDB and EdgeQL"},{"from":29.62960000000018,"to":33.10807500000054,"location":2,"content":"seen a little further uh... than others, "},{"from":33.18315000000036,"to":35.635600000000174,"location":2,"content":"and we think a lot of the reason we've done that is "},{"from":35.635600000000174,"to":39.764725000000176,"location":2,"content":"because uh we're standing on the shoulder of a giant - Postgres."},{"from":39.764725000000176,"to":43.09304999999999,"location":2,"content":"So I'm going to talk about 3 main different things "},{"from":43.09304999999999,"to":46.071025000000354,"location":2,"content":"as to how EdgeQL and EdgeDB leverage Postgres. "},{"from":46.12107500000054,"to":48.49845000000054,"location":2,"content":"First Schema mapping."},{"from":48.49845000000054,"to":51.40135000000017,"location":2,"content":"How we map our notion of schema, "},{"from":51.40135000000017,"to":53.903850000000176,"location":2,"content":"which has been discussed a bunch in previous talks, "},{"from":53.903850000000176,"to":56.406350000000174,"location":2,"content":"onto a PostgreSQL schema. "},{"from":56.45640000000036,"to":58.85879999999999,"location":2,"content":"I'm going to talk about query compilation, "},{"from":58.85879999999999,"to":62.53747500000017,"location":2,"content":"how we take an EdgeQL query and turn it into a PostgreSQL query."},{"from":62.53747500000017,"to":64.76470000000053,"location":2,"content":" I'm gonna talk about uh why Postgres,"},{"from":64.76470000000053,"to":69.66331872500044,"location":2,"content":"why did we build this on Postgres instead of uh on MySQL"},{"from":69.67917556600081,"to":75.35027500000035,"location":2,"content":" or SQL Server or Oracle or Mongo or something."},{"from":75.77570000000054,"to":80.53045000000053,"location":2,"content":"So first schema mapping uh."},{"from":80.53045000000053,"to":83.258175,"location":2,"content":"The... the heart of it is types. "},{"from":83.258175,"to":87.78770000000053,"location":2,"content":"Uh the heart of EdgeDB databases are object types, "},{"from":87.78770000000053,"to":90.99110320299995,"location":2,"content":"and every object type is represented with a SQL table."},{"from":90.99110320300086,"to":95.87077500000126,"location":2,"content":"For every object type in your schema we'll create a PostgreSQL table."},{"from":95.87077500000035,"to":99.77467499999999,"location":2,"content":"But just that's not that interesting. We want to talk about what goes into that table. "},{"from":99.77467499999999,"to":103.85375000000072,"location":2,"content":"So like we talked about, one thing that goes into all of them is an id field, "},{"from":103.85375000000072,"to":106.20530520600057,"location":2,"content":"but on top of that we want to put in all of"},{"from":106.20530520600057,"to":109.15904999999998,"location":2,"content":"the data that was declared in the schema. "},{"from":109.15904999999998,"to":112.43732500000053,"location":2,"content":" So the most basic sort of data that might be on an object"},{"from":112.43732500000053,"to":118.29317499999999,"location":2,"content":"is a single link or a single property that points to 0 or 1 of something else. "},{"from":118.29317499999999,"to":122.74762500000071,"location":2,"content":"And this is mapped into Postgres in a pretty obvious way."},{"from":122.74762500000071,"to":126.95182500000053,"location":2,"content":" Every single property on an EdgeDB object"},{"from":126.95182500000053,"to":129.85472500000017,"location":2,"content":"maps to just a column in the SQL table. "},{"from":129.85472500000017,"to":134.88475000000074,"location":2,"content":"So if we have a property name that holds a string, "},{"from":134.88475000000074,"to":138.4383,"location":2,"content":"that's just a text column in the SQL table."},{"from":138.4383,"to":142.18944840100053,"location":2,"content":" Links are very slightly more interesting."},{"from":142.18944840100053,"to":148.823675,"location":2,"content":"Because we represent links by storing the UUID of the object that's pointed to."},{"from":148.823675,"to":151.85170000000053,"location":2,"content":" So it's just a UUID column. Uh..."},{"from":151.85170000000053,"to":155.330175,"location":2,"content":"In this case, it's a required link, so it's marked as not null. "},{"from":155.330175,"to":160.21005,"location":2,"content":"The most interesting part of this is actually something that isn't here. "},{"from":160.21005,"to":163.38822500000018,"location":2,"content":"You might be used to, in some of your SQL schemas,"},{"from":163.38822500000018,"to":167.2585894980005,"location":2,"content":"having a foreign key specified here saying that"},{"from":167.28361449800013,"to":171.87170000000054,"location":2,"content":" this is a foreign key referencing the primary key of some other table Foo."},{"from":171.87170000000054,"to":175.70052500000037,"location":2,"content":"Uh we don't use that here because we don't need to. "},{"from":175.70052500000037,"to":182.43637712300043,"location":2,"content":"Uh EdgeQL queries, by construction, can't have... can't insert or update"},{"from":182.45988660900048,"to":187.46488660900047,"location":2,"content":" to a... to point to an object that doesn't exist."},{"from":187.52742808800065,"to":192.21702500000035,"location":2,"content":"And so we don't need to use foreign keys to ensure consistency. "},{"from":192.21702500000035,"to":195.79560000000018,"location":2,"content":"Because it's ensured by the type checker, by the query compilation,"},{"from":195.79560000000018,"to":197.6329375020001,"location":2,"content":" by the entire construction of it."},{"from":197.6329375020001,"to":199.98528750200043,"location":2,"content":"And so we get a little performance out of that. "},{"from":199.98528750200043,"to":202.67747500000016,"location":2,"content":"Uh in this example there's one fib I'm telling:"},{"from":202.67747500000016,"to":207.68247500000018,"location":2,"content":"the column names here... I've written nice column names, uh"},{"from":207.76008052799997,"to":211.7115000000007,"location":2,"content":"in the actual implementation, column names are unreadable "},{"from":211.7115000000007,"to":215.29007500000054,"location":2,"content":"machine generated things for technical reasons. "},{"from":215.29007500000054,"to":221.19597500000017,"location":2,"content":"Uh a little more interesting than single links and single properties though are muti-links. "},{"from":221.19597500000017,"to":226.4762500000007,"location":2,"content":"And this is because, like a good SQL.. a good relational database "},{"from":226.4762500000007,"to":228.67845000000054,"location":2,"content":"that stores things in normalized form, "},{"from":228.67845000000054,"to":234.35912500000072,"location":2,"content":"we don't want to store... multiple links in a single table row. "},{"from":234.35912500000072,"to":236.38615000000036,"location":2,"content":"That's not the relational way. "},{"from":236.38615000000036,"to":237.71247500000015,"location":2,"content":"The way you're supposed to do it,"},{"from":237.71247500000015,"to":239.81457500000053,"location":2,"content":"the way that Postgres is optimized for doing it,"},{"from":239.81457500000053,"to":245.69545000000053,"location":2,"content":" is to create a link table, a separate table that maps as a relation between"},{"from":245.69545000000053,"to":248.54829999999998,"location":2,"content":"the source of a multi-link and the things that it's pointing to."},{"from":248.67342499999998,"to":250.83601442900047,"location":2,"content":"And so when you declare a multi-link like this, "},{"from":250.83601442900047,"to":254.80454999999998,"location":2,"content":"we will create a link table that has a source and a target field,"},{"from":254.80454999999998,"to":258.115661804,"location":2,"content":" from... from the source of the link to the targets of the link."},{"from":258.1790010800008,"to":260.03477500000037,"location":2,"content":"We put a unique constraint on it, "},{"from":260.03477500000037,"to":263.8385750000005,"location":2,"content":"because all the links are unique and that helps drive an index."},{"from":263.8385750000005,"to":267.7675000000007,"location":2,"content":"And we also add a reverse index here on the target"},{"from":267.7675000000007,"to":273.25963364700044,"location":2,"content":"so that you can go from targets to sources using EdgeQL backlinks."},{"from":274.4742000000005,"to":276.77650000000074,"location":2,"content":"Uh."},{"from":276.77650000000074,"to":279.4041250000007,"location":2,"content":"There are a few other things that we do in the schema."},{"from":279.42915000000033,"to":281.84261305200073,"location":2,"content":"And all of them are mostly pretty straightforward."},{"from":281.84261305200073,"to":286.9037871700011,"location":2,"content":"Indexes in EdgeQL are unsurprisingly realized as indexes in PostgreSQL."},{"from":286.9037871700002,"to":289.63935000000015,"location":2,"content":"Constraints are Postgres constraints."},{"from":289.63935000000015,"to":295.62032500000055,"location":2,"content":"Uh custom scalar types uh are SQL domain types or Postgres domain types. "},{"from":295.62032500000055,"to":298.72342499999996,"location":2,"content":"Uh type inheritance is a little bit more interesting. "},{"from":298.72342499999996,"to":301.90160000000014,"location":2,"content":"So if you remember from our example Netflix schema,"},{"from":301.90160000000014,"to":307.1126368310004,"location":2,"content":"we have a Content abstract type that has descendants - TV Show and Movies,"},{"from":307.14964780500003,"to":310.3100000000007,"location":2,"content":"and you can select content and get data from that. "},{"from":310.3528468040004,"to":313.8135000000007,"location":2,"content":"Uh the way we implement that is we create a Postgres View "},{"from":313.8135000000007,"to":316.0407250000002,"location":2,"content":"that is a union of all its descendants, "},{"from":316.0407250000002,"to":322.2178869910001,"location":2,"content":"so we have a View for Content that unions together the tables for "},{"from":322.2619359960008,"to":328.77845000000053,"location":2,"content":"TV Show and Movie, including all of the columns that are included in Content."},{"from":328.77845000000053,"to":331.255925,"location":2,"content":"Uh, that handles things for the select side."},{"from":331.255925,"to":334.85952500000036,"location":2,"content":"When you're doing inserts or when you're doing updates uh and deletes,"},{"from":334.85952500000036,"to":337.43710000000016,"location":2,"content":"which can update or delete things from multiple ones,"},{"from":337.43710000000016,"to":341.9666250000007,"location":2,"content":"uh, the View doesn't help us and we need to do some actual work on the compiler side"},{"from":341.9666250000007,"to":345.77042199700077,"location":2,"content":"but the key idea is we use a View for most of the important queries."},{"from":345.77042199700077,"to":351.57622500000014,"location":2,"content":"So that covers basically all of the details, or all of the most interesting details,"},{"from":351.57622500000014,"to":354.4791250000007,"location":2,"content":"about how... things go into the schema."},{"from":354.4791250000007,"to":357.0066500000003,"location":2,"content":"And so now we're gonna talk a bit about how..."},{"from":357.0066500000003,"to":360.51015000000035,"location":2,"content":"we go from an EdgeQL query to a PostgreSQL query."},{"from":360.51015000000035,"to":366.41605,"location":2,"content":"Uh, and the compiler is built in a fairly traditional sort of multi-stage step."},{"from":366.41605,"to":369.64427500000033,"location":2,"content":"Uh, I guess I'm fibbing a little here, and that I've written two phases."},{"from":369.64427500000033,"to":376.22585000000015,"location":2,"content":"There's really a phase before it which takes the... textual string of EdgeQL,"},{"from":376.2627688820005,"to":380.60522500000013,"location":2,"content":"and lexes and parses it into EdgeQL AST."},{"from":380.64214288100015,"to":383.4580750000005,"location":2,"content":"And that's like the first sort of any book on compilers, "},{"from":383.4580750000005,"to":385.81042499999995,"location":2,"content":"and I just sort of forgot to put it on the phase list,"},{"from":385.8354500000005,"to":390.1397500000007,"location":2,"content":"because it's also by far the least interesting part about writing... a compiler."},{"from":390.1397500000007,"to":393.41802500000034,"location":2,"content":"Even though we actually did some interesting stuff on that side, "},{"from":393.41802500000034,"to":396.44604999999996,"location":2,"content":"that custom Python parser generator."},{"from":396.5211250000007,"to":399.6492500000007,"location":2,"content":"But, the two main phases as I think about them are "},{"from":399.6492500000007,"to":403.55315000000036,"location":2,"content":"an EdgeQL to intermediate representation phase, "},{"from":403.55315000000036,"to":407.13172500000013,"location":2,"content":"where we compile to uh intermediate representation for our queries "},{"from":407.14764090000045,"to":411.1607500000007,"location":2,"content":"that represents data... represents the query in a more simple way,"},{"from":411.1607500000007,"to":415.51510000000013,"location":2,"content":"and then an intermediate representation to SQL... phase."},{"from":415.5560409000001,"to":418.76835000000017,"location":2,"content":"And we actually have another phase in front of all of that,"},{"from":418.76835000000017,"to":421.8714500000005,"location":2,"content":"for the GraphQL frontend we touched on a bit,"},{"from":421.8714500000005,"to":425.22479999999996,"location":2,"content":"where we will convert from GraphQL to EdgeQL,"},{"from":425.22479999999996,"to":428.8033750000007,"location":2,"content":"uh which is another place where we're using, in addition to the query builder, "},{"from":428.8033750000007,"to":431.85642499999994,"location":2,"content":"EdgeQL as a target language, and having it work pretty well."},{"from":431.85642499999994,"to":434.63420000000053,"location":2,"content":"But, so now I'm gonna dive into some of the actual parts"},{"from":434.63420000000053,"to":438.8384000000003,"location":2,"content":" of the EdgeQL to intermediate representation... step."},{"from":438.8384000000003,"to":444.59415000000035,"location":2,"content":"One of... the sort of first and most core bits is name resolution."},{"from":444.6490388339999,"to":448.2478,"location":2,"content":"We have a query on the left and a schema that it's writing... "},{"from":448.2478,"to":449.7743250000005,"location":2,"content":"that it's referring to on the right,"},{"from":449.7743250000005,"to":453.8030236740007,"location":2,"content":"and we need to figure out what each of the names actually refers to."},{"from":453.8030236740007,"to":460.05960000000016,"location":2,"content":"The query on the left is... a very simplified version of the Netflix content Movie schema,"},{"from":460.05960000000016,"to":465.06460000000015,"location":2,"content":"with just movies and persons, uh and title renamed to name"},{"from":465.06460000000015,"to":469.143675,"location":2,"content":"for reasons I'll get into a... in a minute, for explanatory purposes."},{"from":469.143675,"to":471.9715000000007,"location":2,"content":"Uh so if we run through name resolution,"},{"from":471.9715000000007,"to":476.400925,"location":2,"content":"we quickly figure out that Movie refers to uh the type Movie,"},{"from":476.400925,"to":478.90342499999997,"location":2,"content":"uh actors refers to that link actors,"},{"from":478.90342499999997,"to":484.33385000000015,"location":2,"content":"the name on actors refers to the name that's a property in Person,"},{"from":484.33385000000015,"to":490.665175,"location":2,"content":"and then the name at the bottom of the whole query refers to... uh the property name."},{"from":490.665175,"to":494.4940000000007,"location":2,"content":"This is all pretty straightforward, except for the one interesting bit,"},{"from":494.4940000000007,"to":497.5220250000003,"location":2,"content":"which is how... those names got resolved,"},{"from":497.5220250000003,"to":501.4509500000005,"location":2,"content":"and that ties into another bit that runs basically in parallel "},{"from":501.4509500000005,"to":505.42992499999997,"location":2,"content":"and very interdependent with name resolution of type resolution."},{"from":505.45607512399994,"to":509.2587500000007,"location":2,"content":"And so this is also... uh pretty straightforward to understand."},{"from":509.2587500000007,"to":514.2637500000006,"location":2,"content":"As we're doing it we figure out that Movie refers a Movie, actors refers to a Person."},{"from":514.2637500000006,"to":516.991475,"location":2,"content":"And then because we know that actors refers to a Person, "},{"from":516.991475,"to":521.2957750000003,"location":2,"content":"that allows us to do the name resolution on that name in its shape,"},{"from":521.2957750000003,"to":523.0225000000006,"location":2,"content":"and figure out that name is a str."},{"from":523.0225000000006,"to":526.4008750000006,"location":2,"content":"And similarly this other name... also a str."},{"from":526.4008750000006,"to":529.8543250000005,"location":2,"content":"Uh the literal Dune, unsurprisingly is a str."},{"from":529.8543250000005,"to":533.257725,"location":2,"content":"Uh and then since we have to do this for all the pieces,"},{"from":533.257725,"to":534.7091749999998,"location":2,"content":"in order to verify everything,"},{"from":534.7091749999998,"to":538.5936776220004,"location":2,"content":"uh you know that... that whole expression in the filter is a bool."},{"from":538.5936776220004,"to":546.8462999999999,"location":2,"content":"And... the... most interesting or useful part about this from a... user's perspective"},{"from":546.8462999999999,"to":551.8512999999999,"location":2,"content":"is that all of this type information, the types of all of the fields in these shapes,"},{"from":551.881949619,"to":557.3567999999999,"location":2,"content":"that actors as a Person, and that sub shape contains a thing name with a str,"},{"from":557.3567999999999,"to":561.3107500000007,"location":2,"content":"all of that gets returned to the client if you're using the binary protocol."},{"from":561.3107500000007,"to":566.0404750000001,"location":2,"content":"And so our clients are able to get a very rich understanding of the data,"},{"from":566.0404750000001,"to":570.1445750000005,"location":2,"content":"knowing the types of everything... and the names of everything,"},{"from":570.1445750000005,"to":575.2496749999999,"location":2,"content":"and can provide a very idiomatic interface in their languages."},{"from":575.2496749999999,"to":578.8032250000001,"location":2,"content":"In Python, even without knowing any of the type information in advance,"},{"from":578.8032250000001,"to":581.7061250000006,"location":2,"content":"uh we get all of the things in the right type,"},{"from":581.7061250000006,"to":588.0875000000007,"location":2,"content":"and... can, you know, provide access just with dot access and everything like that."},{"from":588.0875000000007,"to":592.0915000000006,"location":2,"content":"And a lot of other database clients make it much harder "},{"from":592.0915000000006,"to":596.2932095120003,"location":2,"content":"if you want to have the type information."},{"from":596.3707750000002,"to":599.5990000000006,"location":2,"content":"Uh, we also do cardinality inference, "},{"from":599.5990000000006,"to":603.7531500000002,"location":2,"content":"where everything in the query we figure out how many objects it might return."},{"from":603.7531500000002,"to":609.2836749999999,"location":2,"content":"So here Movie, because Movie is a... object that's just a table,"},{"from":609.2836749999999,"to":613.3377250000001,"location":2,"content":"it could be an empty table, it could have one element, it could have a million."},{"from":613.3377250000001,"to":616.5659500000004,"location":2,"content":"We infer the cardinality as many, and many means it could be anything."},{"from":616.5659500000004,"to":619.8192000000005,"location":2,"content":"It could be 0, it could be infinite, not infinite."},{"from":619.8192000000005,"to":624.9296793740002,"location":2,"content":"Uh, actors, because it's just a multi-link, also has cardinality MANY."},{"from":624.9296793740002,"to":632.0814500000005,"location":2,"content":"Um and then... uh name, because it's a single property, "},{"from":632.0814500000005,"to":636.5359000000002,"location":2,"content":"uh and a single required property, it has cardinality ONE."},{"from":636.5359000000002,"to":639.0634249999999,"location":2,"content":"Uh if it was single but not required, "},{"from":639.0634249999999,"to":641.1905499999999,"location":2,"content":"it would have a different cardinality - AT_MOST_ONE,"},{"from":641.1905499999999,"to":643.7681250000006,"location":2,"content":" which means that it could be empty."},{"from":643.7681250000006,"to":648.7731250000006,"location":2,"content":"Uh, we also know that name is ONE, because it's a required single property."},{"from":648.7731250000006,"to":655.00435,"location":2,"content":"And the literal Dune also has cardinality ONE, because it's a literal string."},{"from":655.00435,"to":659.1835250000003,"location":2,"content":"Uh, but then the most interesting part about this is that"},{"from":659.1835250000003,"to":664.638975000001,"location":2,"content":"we also... because we've done this filter on an exclusively constrained name,"},{"from":664.638975000001,"to":667.5418750000006,"location":2,"content":"because uh in this example each movie... "},{"from":667.5418750000006,"to":670.269600000001,"location":2,"content":"we can only have uh one movie with each name."},{"from":670.269600000001,"to":672.646975000001,"location":2,"content":"So there can only be one Dune."},{"from":672.646975000001,"to":675.0994249999999,"location":2,"content":"Which obviously is a little counterfactual,"},{"from":675.0994249999999,"to":680.1044249999999,"location":2,"content":"there are several Dunes, and... maybe only one worth having."},{"from":680.1044249999999,"to":685.4347500000006,"location":2,"content":"Um, we can infer that the cardinality of the entire thing is AT_MOST_ONE."},{"from":685.4347500000006,"to":688.2125250000003,"location":2,"content":"It may be empty, but it can't be more than one."},{"from":688.2125250000003,"to":691.6910000000006,"location":2,"content":"Because... uh name is an exclusive constraint."},{"from":691.6910000000006,"to":698.172475000001,"location":2,"content":"Uh, and all of this cardinality inference is used to drive the compilation a little."},{"from":698.172475000001,"to":700.925225000001,"location":2,"content":"And the output format and is returned to the client,"},{"from":700.925225000001,"to":703.6279249999999,"location":2,"content":"so it knows whether there's only one result,"},{"from":703.6279249999999,"to":708.6329249999999,"location":2,"content":"it knows... for each of the fields, how many things can be there."},{"from":708.6579389889994,"to":712.5868750000006,"location":2,"content":"Um, there're a few other things we do. One is uh scope tree inference."},{"from":712.5868750000006,"to":715.2318963790015,"location":2,"content":"Uh this is not gonna be on the test,"},{"from":715.2318963790015,"to":720.2368963790015,"location":2,"content":"but core to the semantics of EdgeDB, on a sort of theoretical semantics level,"},{"from":720.2368963790015,"to":724.2235000000006,"location":2,"content":" is that when we have a common path reference uh in a query, "},{"from":724.2235000000006,"to":726.7760499999998,"location":2,"content":"like movie being referred to in two places here,"},{"from":726.7760499999998,"to":730.8050749999995,"location":2,"content":"unless they're all in aggregates, uh it gets hoisted out."},{"from":730.8050749999995,"to":735.4847500000006,"location":2,"content":"Uh, there's some discussion of this idea in a different form on our website,"},{"from":735.4847500000006,"to":738.6128750000006,"location":2,"content":"but again, it really won't be on the test."},{"from":738.6128750000006,"to":742.091350000001,"location":2,"content":"Um, more interesting maybe is we do a lot of converting"},{"from":742.091350000001,"to":745.6949499999995,"location":2,"content":"complex constructs to more primitive ones."},{"from":745.6949499999995,"to":748.7767006719992,"location":2,"content":"Uh, for example, if we have an array of integers, "},{"from":748.7767006719992,"to":751.725975000001,"location":2,"content":"and we want to cast it to an array of strings."},{"from":751.725975000001,"to":755.5297750000002,"location":2,"content":"Uh, Postgres doesn't have a way to do that all at once."},{"from":755.5297750000002,"to":760.0592999999999,"location":2,"content":"So we have to unpack the array into a set, uh, cast the individual elements,"},{"from":760.0592999999999,"to":762.9371749999999,"location":2,"content":" and then array_agg and get back into an array."},{"from":762.9371749999999,"to":766.4406749999998,"location":2,"content":"Uh, maybe more useful is let's say we have a JSON query argument,"},{"from":766.4406749999998,"to":771.0452750000003,"location":2,"content":"and want that... that represents JSON dictionaries or JSON objects"},{"from":771.0452750000003,"to":772.9471749999999,"location":2,"content":"that have a name and an age field,"},{"from":772.9471749999999,"to":775.1744000000002,"location":2,"content":"and we want to cast that into a named tuple."},{"from":775.1744000000002,"to":778.9781999999996,"location":2,"content":"Uh, we translate that into code that calls json_get."},{"from":778.9781999999996,"to":782.9071250000006,"location":2,"content":"With the different fields we're trying to access and then does the appropriate cast,"},{"from":782.9071250000006,"to":786.1854000000002,"location":2,"content":"so name goes to str and age goes to int64."},{"from":786.1854000000002,"to":789.013225000001,"location":2,"content":"Uh, and a bunch of other stuff lives in this too."},{"from":789.013225000001,"to":793.4176250000006,"location":2,"content":"Uh, we resolve overloaded functions to which of the particular overloads it might be."},{"from":793.4176250000006,"to":797.7469499999995,"location":2,"content":"We inline the code for computed properties and links."},{"from":797.7469499999995,"to":803.1273249999995,"location":2,"content":"We insert implicit limits and type name computations into the queries from the CLI."},{"from":803.1273249999995,"to":805.5046999999995,"location":2,"content":"When you're doing CLI there's usually an implicit limit,"},{"from":805.5046999999995,"to":806.9811749999999,"location":2,"content":"so you don't get overwhelmed with data."},{"from":806.9811749999999,"to":809.7339249999999,"location":2,"content":"And we insert type names into the objects we returned"},{"from":809.7339249999999,"to":812.8120000000006,"location":2,"content":" to provide more useful... results."},{"from":812.8120000000006,"to":814.6137999999999,"location":2,"content":"And most of our error checking lives here."},{"from":814.6137999999999,"to":816.8160000000006,"location":2,"content":"Because the earlier in the pipeline it lives, "},{"from":816.8160000000006,"to":823.7229000000002,"location":2,"content":"the... better... source uh information we can give about where the problem came from."},{"from":823.7229000000002,"to":831.1030888159994,"location":2,"content":"Uh, next, after that we have to compile from the intermediate representation to SQL."},{"from":831.1030888159994,"to":835.2093750000006,"location":2,"content":"Uh, and so we generate that SQL based on the intermediate representation"},{"from":835.2093750000006,"to":838.7629249999999,"location":2,"content":"and this scope tree that we've built that represents that..."},{"from":838.8358078099996,"to":843.3425000000005,"location":2,"content":"um not on the test lifting of common path references."},{"from":843.3425000000005,"to":847.821975000001,"location":2,"content":"Um, and, the compilers actually structured in a kind of interesting way."},{"from":847.821975000001,"to":851.0251749999999,"location":2,"content":"We build the query in a way that I will call semi-lazily."},{"from":851.0251749999999,"to":855.6297750000002,"location":2,"content":"In which um, you have uh, when you have stuff like a..."},{"from":855.6297750000002,"to":857.5316749999998,"location":2,"content":"object reference and then a shape on it,"},{"from":857.5316749999998,"to":859.3334750000009,"location":2,"content":"when we go down to compile the object, "},{"from":859.3334750000009,"to":863.2874249999999,"location":2,"content":"we generate code that joins in a subquery that joins in the tables being accessed,"},{"from":863.2874249999999,"to":865.9651000000009,"location":2,"content":"but doesn't add any column accesses to it."},{"from":865.9651000000009,"to":869.7689000000003,"location":2,"content":"And then once we go... to compile the shape,"},{"from":869.7689000000003,"to":873.973100000001,"location":2,"content":"we see \"oh we're trying to access these things on this relation we grabbed over there, "},{"from":873.973100000001,"to":877.4515749999995,"location":2,"content":"so now we need to go back in there and add in the column accesses.\""},{"from":877.4515749999995,"to":881.7058249999995,"location":2,"content":"That sort of... builds it up and then pulls the data out later."},{"from":881.7058249999995,"to":887.5866999999995,"location":2,"content":"And then importantly we serialize all the results into... the appropriate output format."},{"from":887.5866999999995,"to":890.6647750000002,"location":2,"content":"Uh, which is a good time to talk about output formats."},{"from":890.6647750000002,"to":894.3935000000006,"location":2,"content":"So the main output format we have is a binary format."},{"from":894.3935000000006,"to":897.2463500000009,"location":2,"content":"Our data is returned as native PostgreSQL types."},{"from":897.2463500000009,"to":903.3024000000003,"location":2,"content":"Uh, strs are returned as text. Uh..., int64s are returned as int8s."},{"from":903.3024000000003,"to":906.4805749999995,"location":2,"content":"Uh object shapes get returned as tuples."},{"from":906.4805749999995,"to":910.0091000000009,"location":2,"content":"Um, and properties and links that have multiple elements, "},{"from":910.0091000000009,"to":913.4625499999999,"location":2,"content":"which is one of the core things that we make it easy to do, "},{"from":913.4625499999999,"to":918.0421250000006,"location":2,"content":"um... when you're selecting like the shape of, you know, movie and all the actors,"},{"from":918.0421250000006,"to":919.9190000000006,"location":2,"content":"that actors field has multiple elements,"},{"from":919.9190000000006,"to":922.9720499999999,"location":2,"content":"we represent that as an array which will be an array of tuples here."},{"from":922.9720499999999,"to":925.1241999999995,"location":2,"content":"Um, and like I touched on before, "},{"from":925.1241999999995,"to":929.0281000000009,"location":2,"content":"the client is returned a type descriptor describing all this output."},{"from":929.0281000000009,"to":933.7077750000002,"location":2,"content":"Uh, we also importantly have a direct JSON output mode. "},{"from":933.7077750000002,"to":939.1882500000006,"location":2,"content":"Uh, where instead of this binary format we output all the data directly as JSON."},{"from":939.1882500000006,"to":943.1171749999999,"location":2,"content":"Uh, this is... less rich from a client perspective."},{"from":943.1171749999999,"to":947.1211749999999,"location":2,"content":"It's... if you're trying to operate on the data from the direct client,"},{"from":947.1211749999999,"to":948.7227750000002,"location":2,"content":"it's probably not what you want."},{"from":948.7227750000002,"to":951.5005499999999,"location":2,"content":"But, if you just want a really simple, "},{"from":951.5005499999999,"to":955.2292750000003,"location":2,"content":"if you want to make a query and then return if from an endpoint,"},{"from":955.2547924920009,"to":958.8579000000002,"location":2,"content":"uh, without doing any real server side processing,"},{"from":958.8579000000002,"to":961.5105499999999,"location":2,"content":"the JSON mode is a really nice fast way to do that."},{"from":961.5105499999999,"to":965.2893249999995,"location":2,"content":"You... get all the JSON produced efficiently in the database query,"},{"from":965.2893249999995,"to":968.2672999999999,"location":2,"content":"and don't need to do it on your app server."},{"from":968.2672999999999,"to":971.3453750000006,"location":2,"content":"Uh, so that let us put sort of this all together."},{"from":971.3453750000006,"to":978.9029249999999,"location":2,"content":"Um, with a movie query that.. selects the name and the runtime of a movie,"},{"from":978.9029249999999,"to":981.5055250000003,"location":2,"content":"and uh the cast and their names."},{"from":981.5055250000003,"to":985.5846000000009,"location":2,"content":"And uh, approximately the query on the right is what we get."},{"from":985.5846000000009,"to":988.9880000000006,"location":2,"content":"I... I cleaned it up a little bit, to fit on the slide,"},{"from":988.9880000000006,"to":991.0650749999995,"location":2,"content":"um, some of the rough edges."},{"from":991.0650749999995,"to":994.093100000001,"location":2,"content":"But, the nice thing is even if we generate code with rough edges,"},{"from":994.093100000001,"to":1000.0490499999999,"location":2,"content":"Postgres is very good at removing... um things like an extra select around things or."},{"from":1000.0490499999999,"to":1002.8268249999995,"location":2,"content":"Uh, but basically here the idea right here down at the bottom."},{"from":1002.8268249999995,"to":1006.6556500000003,"location":2,"content":"You can see us uh... selecting from a Movie table,"},{"from":1006.6556500000003,"to":1011.5105000000005,"location":2,"content":"and then our... we're returning a tuple containing all the fields we want,"},{"from":1011.5105000000005,"to":1013.8378249999995,"location":2,"content":"including in the subquery. "},{"from":1013.8378249999995,"to":1020.369350000001,"location":2,"content":"We're doing an array_agg of uh... all we select from a link to the cast,"},{"from":1020.369350000001,"to":1025.07405,"location":2,"content":"joined against persons, and build all that up."},{"from":1025.07405,"to":1028.377350000001,"location":2,"content":"All right. So now that I've talked about how Postgres."},{"from":1028.377350000001,"to":1030.1541250000005,"location":2,"content":"I'm gonna talk about why Postgres."},{"from":1030.1541250000005,"to":1034.3332999999998,"location":2,"content":"Uh first... an important thing is we target PostgreSQL,"},{"from":1034.3332999999998,"to":1037.1110749999996,"location":2,"content":"we don't target uh \"SQL\"."},{"from":1037.1110749999996,"to":1038.212175,"location":2,"content":"And there are a few reasons for that."},{"from":1038.212175,"to":1041.4404000000002,"location":2,"content":"One, Postgres is an extremely powerful query engine."},{"from":1041.4404000000002,"to":1046.7206749999998,"location":2,"content":"Uh you can give it complex queries and it... does a great job uh... running them."},{"from":1046.7206749999998,"to":1049.8487999999998,"location":2,"content":"Uh it has important features like lateral join,"},{"from":1049.8487999999998,"to":1052.976925,"location":2,"content":"and it has them for a long time and well optimized."},{"from":1052.976925,"to":1057.9819249999998,"location":2,"content":"Um, and in general, targeting one engine allows us to be much better focused."},{"from":1057.9819249999998,"to":1061.21015,"location":2,"content":"Uh, we can take advantage of the one database we're supporting,"},{"from":1061.21015,"to":1064.5885250000001,"location":2,"content":"and design something that really pushes it to its limits."},{"from":1064.5885250000001,"to":1065.9899249999999,"location":2,"content":"Instead of being spread thin, "},{"from":1065.9899249999999,"to":1068.9929249999998,"location":2,"content":"trying to target the intersection of a bunch of different databases."},{"from":1068.9929249999998,"to":1072.9468750000005,"location":2,"content":"But even with all that, we push Postgres pretty hard."},{"from":1072.9468750000005,"to":1076.3002250000009,"location":2,"content":"Uh, so one example of a thing that works great in Postgres, "},{"from":1076.3002250000009,"to":1078.6025250000002,"location":2,"content":"but maybe wouldn't in other places, is arrays."},{"from":1078.6025250000002,"to":1080.5795000000005,"location":2,"content":"Which we make a very heavy use of"},{"from":1080.5795000000005,"to":1085.1590749999996,"location":2,"content":" both as part of our binary output format and uh sometimes internally."},{"from":1085.1590749999996,"to":1087.5364499999994,"location":2,"content":"Uh, but arrays are not a universal feature."},{"from":1087.5364499999994,"to":1092.6915999999992,"location":2,"content":"Uh, MySQL... sort of has arrays, as of a couple years ago."},{"from":1092.6915999999992,"to":1097.8467500000006,"location":2,"content":"But, only in that is has JSON, and a JSON value could be an array of JSON."},{"from":1097.8467500000006,"to":1103.1770749999994,"location":2,"content":"But there's no way to have an array of integers or an array of tuples."},{"from":1103.1770749999994,"to":1109.4833750000005,"location":2,"content":"Uh, and speaking of tuples, EdgeDB supports ad-hoc tuple types."},{"from":1109.4833750000005,"to":1114.2631500000002,"location":2,"content":"Which SQL actually has very support... very poor support for operating on."},{"from":1114.2631500000002,"to":1117.2661500000002,"location":2,"content":"Uh, Postgres is just good enough. "},{"from":1117.2661500000002,"to":1119.1430250000003,"location":2,"content":"And a lot of other databases, I think, "},{"from":1119.1430250000003,"to":1124.1480250000002,"location":2,"content":"aren't to do things like access an element from a... tuple."},{"from":1124.1480250000002,"to":1128.6525250000002,"location":2,"content":"Uh, but even in Postgres it requires some kind of silly things."},{"from":1128.6525250000002,"to":1132.656525,"location":2,"content":"Uh, to project out of a tuple, we actually pack it into an array,"},{"from":1132.656525,"to":1137.3862500000005,"location":2,"content":"then we unnest that array and read the element out of the column definition list."},{"from":1137.3862500000005,"to":1142.19105,"location":2,"content":"Uh, though... I think Elvis actually had to submit a patch upstream to get that to work."},{"from":1142.19105,"to":1146.1450000000007,"location":2,"content":"Uh, Postgres also has transactional DDL."},{"from":1146.1450000000007,"to":1151.1500000000005,"location":2,"content":"Um, so... changes to the schema can be made inside transactions."},{"from":1151.1500000000005,"to":1155.6795250000002,"location":2,"content":"Then of course since they're in transactions, they can be rolled back if they needed to be."},{"from":1155.6795250000002,"to":1157.9818249999994,"location":2,"content":"This is really important for a couple of reasons."},{"from":1157.9818249999994,"to":1160.1590000000006,"location":2,"content":"One, a migration step could fail."},{"from":1160.1590000000006,"to":1166.1149499999995,"location":2,"content":"Uh, you could have some... cast in there or some... some check that fails,"},{"from":1166.1149499999995,"to":1169.2931250000006,"location":2,"content":"and you need to give up and roll back."},{"from":1169.2931250000006,"to":1174.2230499999998,"location":2,"content":"Uh, and it might be possible to implement ourselves on top of non-transactional DDL,"},{"from":1174.2230499999998,"to":1178.151975000001,"location":2,"content":"but... really wouldn't want to."},{"from":1178.151975000001,"to":1183.2821000000008,"location":2,"content":"But more importantly than that, partially completed migrations uh shouldn't be visible."},{"from":1183.2821000000008,"to":1188.7625749999995,"location":2,"content":"Uh, if there's a migration in progress that shouldn't be apparent to any client,"},{"from":1188.7625749999995,"to":1194.8186250000006,"location":2,"content":"because the... the database just isn't in... um a reasonable state."},{"from":1194.8186250000006,"to":1198.547350000001,"location":2,"content":"Unfortunately, lots of other databases, other than Postgres, "},{"from":1198.547350000001,"to":1200.1489499999996,"location":2,"content":"don't support transactional DDL."},{"from":1200.1489499999996,"to":1201.675475000001,"location":2,"content":"Which would make it really hard for us"},{"from":1201.675475000001,"to":1205.4792750000001,"location":2,"content":"to provide the experience we want for migrations."},{"from":1205.4792750000001,"to":1208.307100000001,"location":2,"content":"Well, but separate from that, Postgres is just good."},{"from":1208.307100000001,"to":1210.9597500000004,"location":2,"content":"It's a good query engine, it's a good database."},{"from":1210.9597500000004,"to":1216.5403249999995,"location":2,"content":"Uh, the biggest complaint we have about it is that we don't think SQL is... "},{"from":1216.5403249999995,"to":1220.0938750000005,"location":2,"content":"uh really stands up in a modern light anymore."},{"from":1220.0938750000005,"to":1224.4982750000001,"location":2,"content":"But... uh, but Postgres still does a great job."},{"from":1224.4982750000001,"to":1226.575350000001,"location":2,"content":"And we think that, among other things, "},{"from":1226.575350000001,"to":1230.5793500000009,"location":2,"content":"EdgeDB actually really helps Postgres shine through."}]} \ No newline at end of file +{ + "font_size": 0.4, + "font_color": "#FFFFFF", + "background_alpha": 0.5, + "background_color": "#9C27B0", + "Stroke": "none", + "body": [ + { + "from": 6.781775, + "to": 8.45, + "location": 2, + "content": "好的。\nAll right." + }, + { + "from": 9.434425, + "to": 15.440425, + "location": 2, + "content": "嗨,大家好,如 Colin 所说,我是从学术界回归的 Michael。\nHi everyone, um and like Colin said, I'm a recovering academic, I've got my uh" + }, + { + "from": 15.440425, + "to": 18.81879899900057, + "location": 2, + "content": "我已经完成了我的论文答辩,祝我好运吧。\nclicker from my thesis defense which is hopefully good luck." + }, + { + "from": 18.893968093000378, + "to": 22.24722500000018, + "location": 2, + "content": "我们今天要讨论的是话题是“编译 EdgeQL”,\nI'm going to be talking about compiling EdgeQL, " + }, + { + "from": 22.24722500000018, + "to": 25.575549999999996, + "location": 2, + "content": "副标题是“站在巨人的肩膀上”。\nwhich is subtitled uh, Standing on the shoulders of giants. " + }, + { + "from": 25.675650000000363, + "to": 29.62960000000018, + "location": 2, + "content": "因为我们认为,之所以 EdgeDB 和 EdgeQL\nBecause uh we think we have with EdgeDB and EdgeQL" + }, + { + "from": 29.62960000000018, + "to": 33.10807500000054, + "location": 2, + "content": "可以看的稍远一点,呃... 相较于其他同类型产品,\nseen a little further uh... than others, " + }, + { + "from": 33.18315000000036, + "to": 35.635600000000174, + "location": 2, + "content": "我们认为能做到这一点的很大一部分原因是:\nand we think a lot of the reason we've done that is " + }, + { + "from": 35.635600000000174, + "to": 39.764725000000176, + "location": 2, + "content": "我们站在 Postgres 这个巨人的肩膀上。\nbecause uh we're standing on the shoulder of a giant - Postgres." + }, + { + "from": 39.764725000000176, + "to": 43.09304999999999, + "location": 2, + "content": "我今天准备讨论 3 大点,\nSo I'm going to talk about 3 main different things " + }, + { + "from": 43.09304999999999, + "to": 46.071025000000354, + "location": 2, + "content": "关于 EdgeDB 和 EdgeQL 是如何充分利用 Postgres 的。\nas to how EdgeQL and EdgeDB leverage Postgres. " + }, + { + "from": 46.12107500000054, + "to": 48.49845000000054, + "location": 2, + "content": "首先,Schema 映射。\nFirst Schema mapping." + }, + { + "from": 48.49845000000054, + "to": 51.40135000000017, + "location": 2, + "content": "我们将讨论如何将我们概念中的 schema,\nHow we map our notion of schema, " + }, + { + "from": 51.40135000000017, + "to": 53.903850000000176, + "location": 2, + "content": "前面的演讲中有讨论过 schema,\nwhich has been discussed a bunch in previous talks, " + }, + { + "from": 53.903850000000176, + "to": 56.406350000000174, + "location": 2, + "content": "映射到 PostgreSQL 中的 schema。\nonto a PostgreSQL schema. " + }, + { + "from": 56.45640000000036, + "to": 58.85879999999999, + "location": 2, + "content": "我将讨论一下查询编译,\nI'm going to talk about query compilation, " + }, + { + "from": 58.85879999999999, + "to": 62.53747500000017, + "location": 2, + "content": "我们是如何将一个 EdgeQL 的查询语句变成一个 PostgreSQL 的查询语句的。\nhow we take an EdgeQL query and turn it into a PostgreSQL query." + }, + { + "from": 62.53747500000017, + "to": 64.76470000000053, + "location": 2, + "content": "我也会讨论一下选择 Postgres 的原因,\n I'm gonna talk about uh why Postgres," + }, + { + "from": 64.76470000000053, + "to": 69.66331872500044, + "location": 2, + "content": "我们为什么在 Postgres 之上构建这些,\nwhy did we build this on Postgres instead of uh on MySQL" + }, + { + "from": 69.67917556600081, + "to": 75.35027500000035, + "location": 2, + "content": "而不是 MySQL、SQL Server、Oracle 或者 Mongo 或者其他的数据库。\n or SQL Server or Oracle or Mongo or something." + }, + { + "from": 75.77570000000054, + "to": 80.53045000000053, + "location": 2, + "content": "好的,第一点,schema 映射。\nSo first schema mapping uh." + }, + { + "from": 80.53045000000053, + "to": 83.258175, + "location": 2, + "content": "Schema 的核心是类型。\nThe... the heart of it is types. " + }, + { + "from": 83.258175, + "to": 87.78770000000053, + "location": 2, + "content": "嗯,EdgeDB 数据库的核心是对象类型,\nUh the heart of EdgeDB databases are object types, " + }, + { + "from": 87.78770000000053, + "to": 90.99110320299995, + "location": 2, + "content": "每一个对象类型对应着一张 SQL 表。\nand every object type is represented with a SQL table." + }, + { + "from": 90.99110320300086, + "to": 95.87077500000126, + "location": 2, + "content": "对你的 schema 中的每个对象类型,我们都会创建一个对应的 PostgreSQL 表。\nFor every object type in your schema we'll create a PostgreSQL table." + }, + { + "from": 95.87077500000035, + "to": 99.77467499999999, + "location": 2, + "content": "当然,如果只是这样的话,也没什么意思。我们还要看一下那张表里有什么。\nBut just that's not that interesting. We want to talk about what goes into that table. " + }, + { + "from": 99.77467499999999, + "to": 103.85375000000072, + "location": 2, + "content": "如我们所谈到过的,每张表都有个 id 字段。\nSo like we talked about, one thing that goes into all of them is an id field, " + }, + { + "from": 103.85375000000072, + "to": 106.20530520600057, + "location": 2, + "content": "但除此之外,我们还需要把 schema 中声明的所有数据都放入表中。\nbut on top of that we want to put in all of" + }, + { + "from": 106.20530520600057, + "to": 109.15904999999998, + "location": 2, + "content": "但除此之外,我们还需要把 schema 中声明的所有数据都放入表中。\nthe data that was declared in the schema. " + }, + { + "from": 109.15904999999998, + "to": 112.43732500000053, + "location": 2, + "content": "对象中最基本的数据可能是单链接,\n So the most basic sort of data that might be on an object" + }, + { + "from": 112.43732500000053, + "to": 118.29317499999999, + "location": 2, + "content": "或者单属性,它指向 0 或 1 个别的东西。\nis a single link or a single property that points to 0 or 1 of something else. " + }, + { + "from": 118.29317499999999, + "to": 122.74762500000071, + "location": 2, + "content": "这跟 Postgres 中的方式有很明显的对应关系。\nAnd this is mapped into Postgres in a pretty obvious way." + }, + { + "from": 122.74762500000071, + "to": 126.95182500000053, + "location": 2, + "content": "每一个 EdgeDB 对象中的单属性,\n Every single property on an EdgeDB object" + }, + { + "from": 126.95182500000053, + "to": 129.85472500000017, + "location": 2, + "content": "恰好对应 SQL 表中的一列。\nmaps to just a column in the SQL table. " + }, + { + "from": 129.85472500000017, + "to": 134.88475000000074, + "location": 2, + "content": "所以,假设我们有一个 string 类型的属性 —— name,\nSo if we have a property name that holds a string, " + }, + { + "from": 134.88475000000074, + "to": 138.4383, + "location": 2, + "content": "对应到 SQL 表中就是一个 text 类型的列。\nthat's just a text column in the SQL table." + }, + { + "from": 138.4383, + "to": 142.18944840100053, + "location": 2, + "content": "链接则稍微更有意思一点。\n Links are very slightly more interesting." + }, + { + "from": 142.18944840100053, + "to": 148.823675, + "location": 2, + "content": "我们通过保存链接所指向的对象的 UUID 来表示链接。\nBecause we represent links by storing the UUID of the object that's pointed to." + }, + { + "from": 148.823675, + "to": 151.85170000000053, + "location": 2, + "content": "所以,链接只是一个 UUID 列。嗯 ...\n So it's just a UUID column. Uh..." + }, + { + "from": 151.85170000000053, + "to": 155.330175, + "location": 2, + "content": "在这个例子里,它是一个必需项,所以我们给它标记了非空。\nIn this case, it's a required link, so it's marked as not null. " + }, + { + "from": 155.330175, + "to": 160.21005, + "location": 2, + "content": "最有意思的地方实际上还不是这里。\nThe most interesting part of this is actually something that isn't here. " + }, + { + "from": 160.21005, + "to": 163.38822500000018, + "location": 2, + "content": "在一些 SQL schema 中,\nYou might be used to, in some of your SQL schemas," + }, + { + "from": 163.38822500000018, + "to": 167.2585894980005, + "location": 2, + "content": "你可能习惯于在这种情况下使用外键,标明这是一个外键,\nhaving a foreign key specified here saying that" + }, + { + "from": 167.28361449800013, + "to": 171.87170000000054, + "location": 2, + "content": "它引用了另外一个表 Foo 的主键。\n this is a foreign key referencing the primary key of some other table Foo." + }, + { + "from": 171.87170000000054, + "to": 175.70052500000037, + "location": 2, + "content": "这里我们没有使用外键,因为不需要。\nUh we don't use that here because we don't need to. " + }, + { + "from": 175.70052500000037, + "to": 182.43637712300043, + "location": 2, + "content": "在构建层面,EdgeQL 查询不能有 ... 不能插入或更新到\nUh EdgeQL queries, by construction, can't have... can't insert or update" + }, + { + "from": 182.45988660900048, + "to": 187.46488660900047, + "location": 2, + "content": "... 不能指向一个不存在的对象。\n to a... to point to an object that doesn't exist." + }, + { + "from": 187.52742808800065, + "to": 192.21702500000035, + "location": 2, + "content": "所以我们不需要使用外键来保证一致性。\nAnd so we don't need to use foreign keys to ensure consistency. " + }, + { + "from": 192.21702500000035, + "to": 195.79560000000018, + "location": 2, + "content": "因为这已经由检查器、查询编译\nBecause it's ensured by the type checker, by the query compilation," + }, + { + "from": 195.79560000000018, + "to": 197.6329375020001, + "location": 2, + "content": "以及它的整个结构来保证了。\n by the entire construction of it." + }, + { + "from": 197.6329375020001, + "to": 199.98528750200043, + "location": 2, + "content": "我们也因此获得了一点性能提升。\nAnd so we get a little performance out of that. " + }, + { + "from": 199.98528750200043, + "to": 202.67747500000016, + "location": 2, + "content": "在这个示例中,我撒了个小慌:\nUh in this example there's one fib I'm telling:" + }, + { + "from": 202.67747500000016, + "to": 207.68247500000018, + "location": 2, + "content": "关于这里的列名,我写的列名清晰易懂,\nthe column names here... I've written nice column names, uh" + }, + { + "from": 207.76008052799997, + "to": 211.7115000000007, + "location": 2, + "content": "但实际实现中,由于一些技术原因,\nin the actual implementation, column names are unreadable " + }, + { + "from": 211.7115000000007, + "to": 215.29007500000054, + "location": 2, + "content": "列名是一些机器生成的、不可读内容。\nmachine generated things for technical reasons. " + }, + { + "from": 215.29007500000054, + "to": 221.19597500000017, + "location": 2, + "content": "比单链接和单属性稍微更有意思一点的是多链接。\nUh a little more interesting than single links and single properties though are muti-links. " + }, + { + "from": 221.19597500000017, + "to": 226.4762500000007, + "location": 2, + "content": "这是因为,就像一个优良的 SQL ...\nAnd this is because, like a good SQL.. a good relational database " + }, + { + "from": 226.4762500000007, + "to": 228.67845000000054, + "location": 2, + "content": "优良的、以标准化形式存储数据的关系型数据库一样,\nthat stores things in normalized form, " + }, + { + "from": 228.67845000000054, + "to": 234.35912500000072, + "location": 2, + "content": "我们可不想把多链接存储到数据表的一行里。\nwe don't want to store... multiple links in a single table row. " + }, + { + "from": 234.35912500000072, + "to": 236.38615000000036, + "location": 2, + "content": "那不是关系型的方式。\nThat's not the relational way. " + }, + { + "from": 236.38615000000036, + "to": 237.71247500000015, + "location": 2, + "content": "你应该使用的方式,\nThe way you're supposed to do it," + }, + { + "from": 237.71247500000015, + "to": 239.81457500000053, + "location": 2, + "content": "同时也是 Postgres 优化过的方式,\nthe way that Postgres is optimized for doing it," + }, + { + "from": 239.81457500000053, + "to": 245.69545000000053, + "location": 2, + "content": "是创建一个链接表,一个单独的表,\n is to create a link table, a separate table that maps as a relation between" + }, + { + "from": 245.69545000000053, + "to": 248.54829999999998, + "location": 2, + "content": "表中存储多链接的源和它所指向的目标之间的映射关系。\nthe source of a multi-link and the things that it's pointing to." + }, + { + "from": 248.67342499999998, + "to": 250.83601442900047, + "location": 2, + "content": "所以当你像这样声明一个多链接时,\nAnd so when you declare a multi-link like this, " + }, + { + "from": 250.83601442900047, + "to": 254.80454999999998, + "location": 2, + "content": "我们会创建一个链接表,它有一个源字段和一个目标字段,\nwe will create a link table that has a source and a target field," + }, + { + "from": 254.80454999999998, + "to": 258.115661804, + "location": 2, + "content": "从链接的源到链接的目标。\n from... from the source of the link to the targets of the link." + }, + { + "from": 258.1790010800008, + "to": 260.03477500000037, + "location": 2, + "content": "我们会创建一个唯一性约束,\nWe put a unique constraint on it, " + }, + { + "from": 260.03477500000037, + "to": 263.8385750000005, + "location": 2, + "content": "因为每一对链接都是唯一的,这有助于我们建立索引。\nbecause all the links are unique and that helps drive an index." + }, + { + "from": 263.8385750000005, + "to": 267.7675000000007, + "location": 2, + "content": "我们也给链接目标创建了反向索引,\nAnd we also add a reverse index here on the target" + }, + { + "from": 267.7675000000007, + "to": 273.25963364700044, + "location": 2, + "content": "这样你可以使用 EdgeQL 的反向链接从链接目标找到链接源。\nso that you can go from targets to sources using EdgeQL backlinks." + }, + { + "from": 274.4742000000005, + "to": 276.77650000000074, + "location": 2, + "content": "嗯。\nUh." + }, + { + "from": 276.77650000000074, + "to": 279.4041250000007, + "location": 2, + "content": "在 schema 中我们也做了一些别的事情。\nThere are a few other things that we do in the schema." + }, + { + "from": 279.42915000000033, + "to": 281.84261305200073, + "location": 2, + "content": "大部分情况下他们都比较直白易懂。\nAnd all of them are mostly pretty straightforward." + }, + { + "from": 281.84261305200073, + "to": 286.9037871700011, + "location": 2, + "content": "不出所料,EdgeQL 中的索引就是 PostgreSQL 中的索引。\nIndexes in EdgeQL are unsurprisingly realized as indexes in PostgreSQL." + }, + { + "from": 286.9037871700002, + "to": 289.63935000000015, + "location": 2, + "content": "约束是 Postgres 中的约束。\nConstraints are Postgres constraints." + }, + { + "from": 289.63935000000015, + "to": 295.62032500000055, + "location": 2, + "content": "自定义标量类型就是 SQL 或 Postgres 中的域类型。\nUh custom scalar types uh are SQL domain types or Postgres domain types. " + }, + { + "from": 295.62032500000055, + "to": 298.72342499999996, + "location": 2, + "content": "类型继承也很有意思。\nUh type inheritance is a little bit more interesting. " + }, + { + "from": 298.72342499999996, + "to": 301.90160000000014, + "location": 2, + "content": "如果你还记得我们的 Netflix schema,\nSo if you remember from our example Netflix schema," + }, + { + "from": 301.90160000000014, + "to": 307.1126368310004, + "location": 2, + "content": "我们有一个抽象类型 Content,它有一些子类型 —— TV Show 和 Movie,\nwe have a Content abstract type that has descendants - TV Show and Movies," + }, + { + "from": 307.14964780500003, + "to": 310.3100000000007, + "location": 2, + "content": "你可以查询 Content 并从中获取数据。\nand you can select content and get data from that. " + }, + { + "from": 310.3528468040004, + "to": 313.8135000000007, + "location": 2, + "content": "我们实现的方式是创建一个 Postgres 的 View,\nUh the way we implement that is we create a Postgres View " + }, + { + "from": 313.8135000000007, + "to": 316.0407250000002, + "location": 2, + "content": "该 View 是抽象类的所有子类的并集。\nthat is a union of all its descendants, " + }, + { + "from": 316.0407250000002, + "to": 322.2178869910001, + "location": 2, + "content": "也就是说我们有一个代表 Content 的 View,\nso we have a View for Content that unions together the tables for " + }, + { + "from": 322.2619359960008, + "to": 328.77845000000053, + "location": 2, + "content": "它将 TV Show 和 Movie 合并到了一起,包含了 Content 中的所有列。\nTV Show and Movie, including all of the columns that are included in Content." + }, + { + "from": 328.77845000000053, + "to": 331.255925, + "location": 2, + "content": "嗯,我们就是这么处理查询语句的。\nUh, that handles things for the select side." + }, + { + "from": 331.255925, + "to": 334.85952500000036, + "location": 2, + "content": "当你执行插入 ... 当你执行更新或者删除时,\nWhen you're doing inserts or when you're doing updates uh and deletes," + }, + { + "from": 334.85952500000036, + "to": 337.43710000000016, + "location": 2, + "content": "可能需要更新或删除多张表中的数据,\nwhich can update or delete things from multiple ones," + }, + { + "from": 337.43710000000016, + "to": 341.9666250000007, + "location": 2, + "content": "这时候 View 就帮不到我们了,我们需要在编译器中做一些实际的工作。\nuh, the View doesn't help us and we need to do some actual work on the compiler side" + }, + { + "from": 341.9666250000007, + "to": 345.77042199700077, + "location": 2, + "content": "但这里的核心理念是我们用 View 来处理大部分非常重要的查询语句。\nbut the key idea is we use a View for most of the important queries." + }, + { + "from": 345.77042199700077, + "to": 351.57622500000014, + "location": 2, + "content": "以上覆盖到了关于 schema 如何工作的大部分细节,或者说大部分有意思的细节。\nSo that covers basically all of the details, or all of the most interesting details," + }, + { + "from": 351.57622500000014, + "to": 354.4791250000007, + "location": 2, + "content": "以上覆盖到了关于 schema 如何工作的大部分细节,或者说大部分有意思的细节。\nabout how... things go into the schema." + }, + { + "from": 354.4791250000007, + "to": 357.0066500000003, + "location": 2, + "content": "现在,我们将讨论一下\nAnd so now we're gonna talk a bit about how..." + }, + { + "from": 357.0066500000003, + "to": 360.51015000000035, + "location": 2, + "content": "EdgeQL 的查询语句时如何变成一个 Postgres 的查询语句的。\nwe go from an EdgeQL query to a PostgreSQL query." + }, + { + "from": 360.51015000000035, + "to": 366.41605, + "location": 2, + "content": "我们的编译器采用了比较传统的多阶段模式。\nUh, and the compiler is built in a fairly traditional sort of multi-stage step." + }, + { + "from": 366.41605, + "to": 369.64427500000033, + "location": 2, + "content": "呃,这里我又撒了个小慌,我在这里写了“两阶段”。\nUh, I guess I'm fibbing a little here, and that I've written two phases." + }, + { + "from": 369.64427500000033, + "to": 376.22585000000015, + "location": 2, + "content": "其实在这之前还有一个阶段,就是将 EdgeQL 的文本字符串\nThere's really a phase before it which takes the... textual string of EdgeQL," + }, + { + "from": 376.2627688820005, + "to": 380.60522500000013, + "location": 2, + "content": "通过词法分析和语法分析转变成 EdgeQL 的抽象语法树。\nand lexes and parses it into EdgeQL AST." + }, + { + "from": 380.64214288100015, + "to": 383.4580750000005, + "location": 2, + "content": "差不多所有编译器相关的书籍,前三分之一都在讲这个阶段的内容。\nAnd that's like the first third of any book on compilers, " + }, + { + "from": 383.4580750000005, + "to": 385.81042499999995, + "location": 2, + "content": "我忘记把它放到阶段列表里了,\nand I just sort of forgot to put it on the phase list," + }, + { + "from": 385.8354500000005, + "to": 390.1397500000007, + "location": 2, + "content": "因为这也是目前为止写一个编译器时最没意思的部分。\nbecause it's also by far the least interesting part about writing... a compiler." + }, + { + "from": 390.1397500000007, + "to": 393.41802500000034, + "location": 2, + "content": "虽然在这里我们也确实做了一些有意思的东西,\nEven though we actually did some interesting stuff on that side, " + }, + { + "from": 393.41802500000034, + "to": 396.44604999999996, + "location": 2, + "content": "比如那个自定义的 Python 解析器生成器。\nthat custom Python parser generator." + }, + { + "from": 396.5211250000007, + "to": 399.6492500000007, + "location": 2, + "content": "但我认为的两个主要阶段是:\nBut, the two main phases as I think about them are " + }, + { + "from": 399.6492500000007, + "to": 403.55315000000036, + "location": 2, + "content": "EdgeQL 到 IR(中间语言)阶段,\nan EdgeQL to intermediate representation phase, " + }, + { + "from": 403.55315000000036, + "to": 407.13172500000013, + "location": 2, + "content": "在这个阶段我们将查询语句编译成 IR,\nwhere we compile to uh intermediate representation for our queries " + }, + { + "from": 407.14764090000045, + "to": 411.1607500000007, + "location": 2, + "content": "以更简单的方式表示数据 ... 表示查询语句;\nthat represents data... represents the query in a more simple way," + }, + { + "from": 411.1607500000007, + "to": 415.51510000000013, + "location": 2, + "content": "然后是 IR 到 SQL 阶段。\nand then an intermediate representation to SQL... phase." + }, + { + "from": 415.5560409000001, + "to": 418.76835000000017, + "location": 2, + "content": "其实在所有这些之前还有另外一个阶段,\nAnd we actually have another phase in front of all of that," + }, + { + "from": 418.76835000000017, + "to": 421.8714500000005, + "location": 2, + "content": "就是我们前面也有提到过一点的 GraphQL 前端,\nfor the GraphQL frontend we touched on a bit," + }, + { + "from": 421.8714500000005, + "to": 425.22479999999996, + "location": 2, + "content": "在这里把 GraphQL 转变成 EdgeQL。\nwhere we will convert from GraphQL to EdgeQL," + }, + { + "from": 425.22479999999996, + "to": 428.8033750000007, + "location": 2, + "content": "这也是除了查询生成器之外,\nuh which is another place where we're using, in addition to the query builder, " + }, + { + "from": 428.8033750000007, + "to": 431.85642499999994, + "location": 2, + "content": "另一个我们把 EdgeQL 当目标语言的地方,这里的效果也很棒。\nEdgeQL as a target language, and having it work pretty well." + }, + { + "from": 431.85642499999994, + "to": 434.63420000000053, + "location": 2, + "content": "但是,现在我准备深入到实际情况,\nBut, so now I'm gonna dive into some of the actual parts" + }, + { + "from": 434.63420000000053, + "to": 438.8384000000003, + "location": 2, + "content": "讲一下 EdgeQL 到 IR 这个步骤。\n of the EdgeQL to intermediate representation... step." + }, + { + "from": 438.8384000000003, + "to": 444.59415000000035, + "location": 2, + "content": "其中 ... 差不多最优先也是最核心的一点是名称解析。\nOne of... the sort of first and most core bits is name resolution." + }, + { + "from": 444.6490388339999, + "to": 448.2478, + "location": 2, + "content": "在左边我们有一个查询语句,\nWe have a query on the left and a schema that it's writing... " + }, + { + "from": 448.2478, + "to": 449.7743250000005, + "location": 2, + "content": "在右边是它写到 ... 引用到的 schema。\nthat it's referring to on the right," + }, + { + "from": 449.7743250000005, + "to": 453.8030236740007, + "location": 2, + "content": "我们需要搞清楚这里的每一个名称指向的是什么。\nand we need to figure out what each of the names actually refers to." + }, + { + "from": 453.8030236740007, + "to": 460.05960000000016, + "location": 2, + "content": "左边的查询(口误,应是“右边的 schema”)是简化版的 Netflix Movie schema。\nThe query on the left is... a very simplified version of the Netflix content Movie schema," + }, + { + "from": 460.05960000000016, + "to": 465.06460000000015, + "location": 2, + "content": "其中只包含了 Movie 和 Person。另外,title 被重命名为 name,\nwith just movies and persons, uh and title renamed to name" + }, + { + "from": 465.06460000000015, + "to": 469.143675, + "location": 2, + "content": "原因我稍后会解释。\nfor reasons I'll get into a... in a minute, for explanatory purposes." + }, + { + "from": 469.143675, + "to": 471.9715000000007, + "location": 2, + "content": "如果过一下名称解析,\nUh so if we run through name resolution," + }, + { + "from": 471.9715000000007, + "to": 476.400925, + "location": 2, + "content": "我们很快可以明白 Movie 指向的是类型 Movie,\nwe quickly figure out that Movie refers to uh the type Movie," + }, + { + "from": 476.400925, + "to": 478.90342499999997, + "location": 2, + "content": "actors 指向的是那个 actors 链接\nuh actors refers to that link actors," + }, + { + "from": 478.90342499999997, + "to": 484.33385000000015, + "location": 2, + "content": "actors 内的 name 指向的是 Person 的 name 属性,\nthe name on actors refers to the name that's a property in Person," + }, + { + "from": 484.33385000000015, + "to": 490.665175, + "location": 2, + "content": "整个查询最底下的那个 name 指向的是(Movie的) name 属性。\nand then the name at the bottom of the whole query refers to... uh the property name." + }, + { + "from": 490.665175, + "to": 494.4940000000007, + "location": 2, + "content": "这些都很直截了当,除了一个地方有点意思,\nThis is all pretty straightforward, except for the one interesting bit," + }, + { + "from": 494.4940000000007, + "to": 497.5220250000003, + "location": 2, + "content": "那就是我们是怎么分辨那些 name 的。\nwhich is how... those names got resolved," + }, + { + "from": 497.5220250000003, + "to": 501.4509500000005, + "location": 2, + "content": "这关系到另外一个跟名称解析基本上平行进行,\nand that ties into another bit that runs basically in parallel " + }, + { + "from": 501.4509500000005, + "to": 505.42992499999997, + "location": 2, + "content": "并相互依赖的过程 —— 类型解析。\nand very interdependent with name resolution of type resolution." + }, + { + "from": 505.45607512399994, + "to": 509.2587500000007, + "location": 2, + "content": "所以这个也比较简单易懂。\nAnd so this is also... uh pretty straightforward to understand." + }, + { + "from": 509.2587500000007, + "to": 514.2637500000006, + "location": 2, + "content": "当我们做解析时,我们知道 Movie 指向 Movie 类型,actors 指向 Person 类型。\nAs we're doing it we figure out that Movie refers a Movie, actors refers to a Person." + }, + { + "from": 514.2637500000006, + "to": 516.991475, + "location": 2, + "content": "然后,因为我们知道 actors 指向 Person 类型,\nAnd then because we know that actors refers to a Person, " + }, + { + "from": 516.991475, + "to": 521.2957750000003, + "location": 2, + "content": "我们就可以在它的 Shape 内做 name 的名称解析,\nthat allows us to do the name resolution on that name in its shape," + }, + { + "from": 521.2957750000003, + "to": 523.0225000000006, + "location": 2, + "content": "最后推断出 name 是一个 str。\nand figure out that name is a str." + }, + { + "from": 523.0225000000006, + "to": 526.4008750000006, + "location": 2, + "content": "同理,另一个 name ... 也是一个 str。\nAnd similarly this other name... also a str." + }, + { + "from": 526.4008750000006, + "to": 529.8543250000005, + "location": 2, + "content": "字面量 Dune,不出所料,是个 str。\nUh the literal Dune, unsurprisingly is a str." + }, + { + "from": 529.8543250000005, + "to": 533.257725, + "location": 2, + "content": "为了验证所有内容,我们需要对所有的片段做上述操作。\nUh and then since we have to do this for all the pieces," + }, + { + "from": 533.257725, + "to": 534.7091749999998, + "location": 2, + "content": "为了验证所有内容,我们需要对所有的片段做上述操作。\nin order to verify everything," + }, + { + "from": 534.7091749999998, + "to": 538.5936776220004, + "location": 2, + "content": "你应该懂,那个过滤器表达式的结果是 bool。\nuh you know that... that whole expression in the filter is a bool." + }, + { + "from": 538.5936776220004, + "to": 546.8462999999999, + "location": 2, + "content": "从一个用户的角度来说,最有意思或者最有用的部分是\nAnd... the... most interesting or useful part about this from a... user's perspective" + }, + { + "from": 546.8462999999999, + "to": 551.8512999999999, + "location": 2, + "content": "所有这些类型信息,包含那些 Shape 中所有字段的类型、\nis that all of this type information, the types of all of the fields in these shapes," + }, + { + "from": 551.881949619, + "to": 557.3567999999999, + "location": 2, + "content": "actors 是 Person、子 shape 中包含了一个 str 类的 name 等,\nthat actors as a Person, and that sub shape contains a thing name with a str," + }, + { + "from": 557.3567999999999, + "to": 561.3107500000007, + "location": 2, + "content": "如果你使用 binary 协议的话,所有这些类型信息都会返回给客户端。\nall of that gets returned to the client if you're using the binary protocol." + }, + { + "from": 561.3107500000007, + "to": 566.0404750000001, + "location": 2, + "content": "所以我们在客户端也能很详细地了解这些数据,\nAnd so our clients are able to get a very rich understanding of the data," + }, + { + "from": 566.0404750000001, + "to": 570.1445750000005, + "location": 2, + "content": "我们可以知道所有东西的类型、以及所有东西的名称,\nknowing the types of everything... and the names of everything," + }, + { + "from": 570.1445750000005, + "to": 575.2496749999999, + "location": 2, + "content": "我们可以提供符合语言习惯的接口。\nand can provide a very idiomatic interface in their languages." + }, + { + "from": 575.2496749999999, + "to": 578.8032250000001, + "location": 2, + "content": "在 Python 中,即便不提前知道这些类型信息,\nIn Python, even without knowing any of the type information in advance," + }, + { + "from": 578.8032250000001, + "to": 581.7061250000006, + "location": 2, + "content": "我们也能让所以东西的类型都正确,\nuh we get all of the things in the right type," + }, + { + "from": 581.7061250000006, + "to": 588.0875000000007, + "location": 2, + "content": "因此...,可以提供比如点访问这样的功能。\nand... can, you know, provide access just with dot access and everything like that." + }, + { + "from": 588.0875000000007, + "to": 592.0915000000006, + "location": 2, + "content": "在很多别的数据库客户端中,\nAnd a lot of other database clients make it much harder " + }, + { + "from": 592.0915000000006, + "to": 596.2932095120003, + "location": 2, + "content": "想要获得类型信息是很困难的。\nif you want to have the type information." + }, + { + "from": 596.3707750000002, + "to": 599.5990000000006, + "location": 2, + "content": "我们也做了基数推断,\nUh, we also do cardinality inference, " + }, + { + "from": 599.5990000000006, + "to": 603.7531500000002, + "location": 2, + "content": "对于所有查询中的内容,我们都推断出它将返回的对象数量。\nwhere everything in the query we figure out how many objects it might return." + }, + { + "from": 603.7531500000002, + "to": 609.2836749999999, + "location": 2, + "content": "比如这里的 Movie,由于 Movie 是一个对象,其实就是一张表。\nSo here Movie, because Movie is a... object that's just a table," + }, + { + "from": 609.2836749999999, + "to": 613.3377250000001, + "location": 2, + "content": "它可能是张空表,可能有一条数据,也可能有 100 万条。\nit could be an empty table, it could have one element, it could have a million." + }, + { + "from": 613.3377250000001, + "to": 616.5659500000004, + "location": 2, + "content": "我们推断它的基数是 MANY,MANY 代表可能是任何数量。\nWe infer the cardinality as MANY, and MANY means it could be anything." + }, + { + "from": 616.5659500000004, + "to": 619.8192000000005, + "location": 2, + "content": "它可能是 0 条,也可能是无限条,好吧,不会有无限条。\nIt could be 0, it could be infinite, not infinite." + }, + { + "from": 619.8192000000005, + "to": 624.9296793740002, + "location": 2, + "content": "由于 actors 是个多链接,它的基数也是 MANY。\nUh, actors, because it's just a multi-link, also has cardinality MANY." + }, + { + "from": 624.9296793740002, + "to": 632.0814500000005, + "location": 2, + "content": "然后,name,因为它是个单属性,\nUm and then... uh name, because it's a single property, " + }, + { + "from": 632.0814500000005, + "to": 636.5359000000002, + "location": 2, + "content": "一个单一的必需属性,它的基数是 ONE。\nuh and a single required property, it has cardinality ONE." + }, + { + "from": 636.5359000000002, + "to": 639.0634249999999, + "location": 2, + "content": "如果是单一但不必需的话,\nUh if it was single but not required, " + }, + { + "from": 639.0634249999999, + "to": 641.1905499999999, + "location": 2, + "content": "它会有个不一样的基数 —— AT_MOST_ONE,\nit would have a different cardinality - AT_MOST_ONE," + }, + { + "from": 641.1905499999999, + "to": 643.7681250000006, + "location": 2, + "content": "表示它可能为空。\n which means that it could be empty." + }, + { + "from": 643.7681250000006, + "to": 648.7731250000006, + "location": 2, + "content": "我们也能知道 name 的基数是 ONE,因为它是一个必需的单属性。\nUh, we also know that name is ONE, because it's a required single property." + }, + { + "from": 648.7731250000006, + "to": 655.00435, + "location": 2, + "content": "字面量 Dune 的基数也是 ONE,因为它就是个常量字符串。\nAnd the literal Dune also has cardinality ONE, because it's a literal string." + }, + { + "from": 655.00435, + "to": 659.1835250000003, + "location": 2, + "content": "然后,关于这部分,最有意思的地方来了。\nUh, but then the most interesting part about this is that" + }, + { + "from": 659.1835250000003, + "to": 664.638975000001, + "location": 2, + "content": "由于我们过滤的属性 name 具有排他约束,\nwe also... because we've done this filter on an exclusively constrained name," + }, + { + "from": 664.638975000001, + "to": 667.5418750000006, + "location": 2, + "content": "因为在这个例子里,每部电影 ...\nbecause uh in this example each movie... " + }, + { + "from": 667.5418750000006, + "to": 670.269600000001, + "location": 2, + "content": "每个电影名称只能有一部电影,\nwe can only have uh one movie with each name." + }, + { + "from": 670.269600000001, + "to": 672.646975000001, + "location": 2, + "content": "所以只能有一部叫 Dune 的电影。\nSo there can only be one Dune." + }, + { + "from": 672.646975000001, + "to": 675.0994249999999, + "location": 2, + "content": "这显然有点不符合实际情况,\nWhich obviously is a little counterfactual," + }, + { + "from": 675.0994249999999, + "to": 680.1044249999999, + "location": 2, + "content": "实际上有好几部电影叫 Dune,虽然 ... 可能只有一部值得有。\nthere are several Dunes, and... maybe only one worth having." + }, + { + "from": 680.1044249999999, + "to": 685.4347500000006, + "location": 2, + "content": "呃,我们可以推断出这整个查询的基数是 AT_MOST_ONE。\nUm, we can infer that the cardinality of the entire thing is AT_MOST_ONE." + }, + { + "from": 685.4347500000006, + "to": 688.2125250000003, + "location": 2, + "content": "它可能是空,但不会超过 1。\nIt may be empty, but it can't be more than one." + }, + { + "from": 688.2125250000003, + "to": 691.6910000000006, + "location": 2, + "content": "因为 ...,name 有排他约束。\nBecause... uh name is an exclusive constraint." + }, + { + "from": 691.6910000000006, + "to": 698.172475000001, + "location": 2, + "content": "这些基数推断也稍微推动了编译的进行。\nUh, and all of this cardinality inference is used to drive the compilation a little." + }, + { + "from": 698.172475000001, + "to": 700.925225000001, + "location": 2, + "content": "这个输出格式被返回到了客户端,\nAnd the output format and is returned to the client," + }, + { + "from": 700.925225000001, + "to": 703.6279249999999, + "location": 2, + "content": "所以我们能知道它是否只有一个结果,\nso it knows whether there's only one result," + }, + { + "from": 703.6279249999999, + "to": 708.6329249999999, + "location": 2, + "content": "我们也能知道,每个字段能有多少元素。\nit knows... for each of the fields, how many things can be there." + }, + { + "from": 708.6579389889994, + "to": 712.5868750000006, + "location": 2, + "content": "嗯,我们还做了一些别的事情。其中之一是作用域树的推断。\nUm, there're a few other things we do. One is uh scope tree inference." + }, + { + "from": 712.5868750000006, + "to": 715.2318963790015, + "location": 2, + "content": "这不会出现在 TAST(强类型抽象语法树)中,\nUh this is not gonna be on the TAST," + }, + { + "from": 715.2318963790015, + "to": 720.2368963790015, + "location": 2, + "content": "但在理论语义学的层级上,EdgeDB 语义学的核心是\nbut core to the semantics of EdgeDB, on a sort of theoretical semantics level," + }, + { + "from": 720.2368963790015, + "to": 724.2235000000006, + "location": 2, + "content": "当我们在一个查询语句中有相同的路径引用时,\n is that when we have a common path reference uh in a query, " + }, + { + "from": 724.2235000000006, + "to": 726.7760499999998, + "location": 2, + "content": "比如这里 Movie 在两个地方被引用到,\nlike movie being referred to in two places here," + }, + { + "from": 726.7760499999998, + "to": 730.8050749999995, + "location": 2, + "content": "它会被判定为同一目标,除非它们都在聚合函数中。\nunless they're all in aggregates, uh it gets hoisted out." + }, + { + "from": 730.8050749999995, + "to": 735.4847500000006, + "location": 2, + "content": "在我们的网站上有关于这个问题的另一种形式的讨论,\nUh, there's some discussion of this idea in a different form on our website," + }, + { + "from": 735.4847500000006, + "to": 738.6128750000006, + "location": 2, + "content": "但,再强调一次,这个真的不会出现在 TAST 中。\nbut again, it really won't be on the TAST." + }, + { + "from": 738.6128750000006, + "to": 742.091350000001, + "location": 2, + "content": "嗯,更有趣的大概是我们做了大量的转换,\nUm, more interesting maybe is we do a lot of converting" + }, + { + "from": 742.091350000001, + "to": 745.6949499999995, + "location": 2, + "content": "将复杂结构转换成基本结构。\ncomplex constructs to more primitive ones." + }, + { + "from": 745.6949499999995, + "to": 748.7767006719992, + "location": 2, + "content": "比如,如果我们有一个整型数组,\nUh, for example, if we have an array of integers, " + }, + { + "from": 748.7767006719992, + "to": 751.725975000001, + "location": 2, + "content": "我们想将它转型为字符串数组。\nand we want to cast it to an array of strings." + }, + { + "from": 751.725975000001, + "to": 755.5297750000002, + "location": 2, + "content": "Postgres 中就没有能一步完成的方式。\nUh, Postgres doesn't have a way to do that all at once." + }, + { + "from": 755.5297750000002, + "to": 760.0592999999999, + "location": 2, + "content": "我们需要将数组解包成集合,再将每个独立的元素转型,\nSo we have to unpack the array into a set, uh, cast the individual elements," + }, + { + "from": 760.0592999999999, + "to": 762.9371749999999, + "location": 2, + "content": "然后用 array_agg 将之变回一个数组。\n and then array_agg and get back into an array." + }, + { + "from": 762.9371749999999, + "to": 766.4406749999998, + "location": 2, + "content": "呃,可能更有用的是:假设我们有一个 JSON 查询参数,\nUh, maybe more useful is let's say we have a JSON query argument," + }, + { + "from": 766.4406749999998, + "to": 771.0452750000003, + "location": 2, + "content": "想要 ... 它代表一个 JSON 字典或者 JSON 对象,\nand want that... that represents JSON dictionaries or JSON objects" + }, + { + "from": 771.0452750000003, + "to": 772.9471749999999, + "location": 2, + "content": "它有 name 和 age 两个字段,\nthat have a name and an age field," + }, + { + "from": 772.9471749999999, + "to": 775.1744000000002, + "location": 2, + "content": "然后我们想把它转变成一个具名元组。\nand we want to cast that into a named tuple." + }, + { + "from": 775.1744000000002, + "to": 778.9781999999996, + "location": 2, + "content": "呃,我们把这段翻译成代码:\nUh, we translate that into code that calls json_get" + }, + { + "from": 778.9781999999996, + "to": 782.9071250000006, + "location": 2, + "content": "调用 json_get 获得每个我们想访问的字段,然后做相应的转型。\nwith the different fields we're trying to access and then does the appropriate cast," + }, + { + "from": 782.9071250000006, + "to": 786.1854000000002, + "location": 2, + "content": "所以 name 变成了 str 类型,age 变成了 int64 类型。\nso name goes to str and age goes to int64." + }, + { + "from": 786.1854000000002, + "to": 789.013225000001, + "location": 2, + "content": "呃,我们在这里还做了一堆别的事情。\nUh, and a bunch of other stuff lives in this too." + }, + { + "from": 789.013225000001, + "to": 793.4176250000006, + "location": 2, + "content": "我们将重载函数解析为可能的特定函数。\nUh, we resolve overloaded functions to which of the particular overloads it might be." + }, + { + "from": 793.4176250000006, + "to": 797.7469499999995, + "location": 2, + "content": "我们为计算属性和链接做代码内联。\nWe inline the code for computed properties and links." + }, + { + "from": 797.7469499999995, + "to": 803.1273249999995, + "location": 2, + "content": "我们为 CLI 中的查询语句增加隐式的限制和类型名称计算。\nWe insert implicit limits and type name computations into the queries from the CLI." + }, + { + "from": 803.1273249999995, + "to": 805.5046999999995, + "location": 2, + "content": "当你使用 CLI 时,通常都会有一些隐式的限制,\nWhen you're doing CLI there's usually an implicit limit," + }, + { + "from": 805.5046999999995, + "to": 806.9811749999999, + "location": 2, + "content": "这样你才不会被数据淹没。\nso you don't get overwhelmed with data." + }, + { + "from": 806.9811749999999, + "to": 809.7339249999999, + "location": 2, + "content": "为了提供更有用的结果,\nAnd we insert type names into the objects we returned" + }, + { + "from": 809.7339249999999, + "to": 812.8120000000006, + "location": 2, + "content": "我们在要返回的对象中增加类型名称。\n to provide more useful... results." + }, + { + "from": 812.8120000000006, + "to": 814.6137999999999, + "location": 2, + "content": "另外,大部分的错误检查也在这里进行。\nAnd most of our error checking lives here." + }, + { + "from": 814.6137999999999, + "to": 816.8160000000006, + "location": 2, + "content": "因为在流程的越早阶段做,\nBecause the earlier in the pipeline it lives, " + }, + { + "from": 816.8160000000006, + "to": 823.7229000000002, + "location": 2, + "content": "对于问题出在哪儿,我们就越能提供更完善的信息。\nthe... better... source uh information we can give about where the problem came from." + }, + { + "from": 823.7229000000002, + "to": 831.1030888159994, + "location": 2, + "content": "呃,下一个,然后我们需要把 IR 编译为 SQL。\nUh, next, after that we have to compile from the intermediate representation to SQL." + }, + { + "from": 831.1030888159994, + "to": 835.2093750000006, + "location": 2, + "content": "SQL 的生成是基于 IR\nUh, and so we generate that SQL based on the intermediate representation" + }, + { + "from": 835.2093750000006, + "to": 838.7629249999999, + "location": 2, + "content": "以及合并了相同路径引用的、不在 TAST 中的作用域树。\nand this scope tree that we've built that represents that..." + }, + { + "from": 838.8358078099996, + "to": 843.3425000000005, + "location": 2, + "content": "以及合并了相同路径引用的、不在 TAST 中的作用域树。\num not on the TAST, lifting of common path references." + }, + { + "from": 843.3425000000005, + "to": 847.821975000001, + "location": 2, + "content": "呃,然后,我们的编译器实际上是通过一种有点意思的方式组织起来的。\nUm, and, the compilers actually structured in a kind of interesting way." + }, + { + "from": 847.821975000001, + "to": 851.0251749999999, + "location": 2, + "content": "我们通过一种我称之为半懒加载的方式构建查询语句。\nWe build the query in a way that I will call semi-lazily." + }, + { + "from": 851.0251749999999, + "to": 855.6297750000002, + "location": 2, + "content": "在这种方式下,你有 ...,当你有一个类似对象引用的东西,\nIn which um, you have uh, when you have stuff like a..." + }, + { + "from": 855.6297750000002, + "to": 857.5316749999998, + "location": 2, + "content": "然后有个相关的 shape,\nobject reference and then a shape on it," + }, + { + "from": 857.5316749999998, + "to": 859.3334750000009, + "location": 2, + "content": "当我们开始去编译这个对象时,\nwhen we go down to compile the object, " + }, + { + "from": 859.3334750000009, + "to": 863.2874249999999, + "location": 2, + "content": "我们生成代码连接一个子查询,在这个子查询中我们连接那些被访问到的表,\nwe generate code that joins in a subquery that joins in the tables being accessed," + }, + { + "from": 863.2874249999999, + "to": 865.9651000000009, + "location": 2, + "content": "但不会添加任何被访问到的列。\nbut doesn't add any column accesses to it." + }, + { + "from": 865.9651000000009, + "to": 869.7689000000003, + "location": 2, + "content": "然后,当我们开始编译那个 shape 时,\nAnd then once we go... to compile the shape," + }, + { + "from": 869.7689000000003, + "to": 873.973100000001, + "location": 2, + "content": "我们意识到:“哦,我们将从那边取到的关系里访问这些数据,\nwe see \"oh we're trying to access these things on this relation we grabbed over there, " + }, + { + "from": 873.973100000001, + "to": 877.4515749999995, + "location": 2, + "content": "所以现在我们要回到那边,添加要访问的列”。\nso now we need to go back in there and add in the column accesses.\"" + }, + { + "from": 877.4515749999995, + "to": 881.7058249999995, + "location": 2, + "content": "差不多就是先构建起来回头再添加数据的方式。\nThat sort of... builds it up and then pulls the data out later." + }, + { + "from": 881.7058249999995, + "to": 887.5866999999995, + "location": 2, + "content": "然后重要的是我们会将所有结果序列化为合适的输出格式。\nAnd then importantly we serialize all the results into... the appropriate output format." + }, + { + "from": 887.5866999999995, + "to": 890.6647750000002, + "location": 2, + "content": "呃,我们趁机讨论一下输出格式吧。\nUh, which is a good time to talk about output formats." + }, + { + "from": 890.6647750000002, + "to": 894.3935000000006, + "location": 2, + "content": "我们主要的输出格式是二进制格式。\nSo the main output format we have is a binary format." + }, + { + "from": 894.3935000000006, + "to": 897.2463500000009, + "location": 2, + "content": "我们的数据会以原生的 PostgreSQL 类型返回。\nOur data is returned as native PostgreSQL types." + }, + { + "from": 897.2463500000009, + "to": 903.3024000000003, + "location": 2, + "content": "也即,字符串会以 text 类型返回,呃 ...,int64 类型的会以 int8 类型返回。\nUh, strs are returned as text. Uh..., int64s are returned as int8s." + }, + { + "from": 903.3024000000003, + "to": 906.4805749999995, + "location": 2, + "content": "对象的 shape 会以元组类型返回。\nUh object shapes get returned as tuples." + }, + { + "from": 906.4805749999995, + "to": 910.0091000000009, + "location": 2, + "content": "呃,包含多个元素的属性或者链接,\nUm, and properties and links that have multiple elements, " + }, + { + "from": 910.0091000000009, + "to": 913.4625499999999, + "location": 2, + "content": "是我们努力简化的核心之一。\nwhich is one of the core things that we make it easy to do, " + }, + { + "from": 913.4625499999999, + "to": 918.0421250000006, + "location": 2, + "content": "比如说,当你查询 Movie shape 和它所有的 actors 时,\num... when you're selecting like the shape of, you know, movie and all the actors," + }, + { + "from": 918.0421250000006, + "to": 919.9190000000006, + "location": 2, + "content": "这里的 actors 字段包含多个元素,\nthat actors field has multiple elements," + }, + { + "from": 919.9190000000006, + "to": 922.9720499999999, + "location": 2, + "content": "这种情况下我们使用一个数组来表示它,一个包含元组的数组。\nwe represent that as an array which will be an array of tuples here." + }, + { + "from": 922.9720499999999, + "to": 925.1241999999995, + "location": 2, + "content": "嗯,就想我之前提到的,\nUm, and like I touched on before, " + }, + { + "from": 925.1241999999995, + "to": 929.0281000000009, + "location": 2, + "content": "我们会返回一个包含了所有这些输出内容的类型描述符给客户端。\nthe client is returned a type descriptor describing all this output." + }, + { + "from": 929.0281000000009, + "to": 933.7077750000002, + "location": 2, + "content": "呃,另外,重要的是我们也有一个直接输出 JSON 数据的模式。\nUh, we also importantly have a direct JSON output mode. " + }, + { + "from": 933.7077750000002, + "to": 939.1882500000006, + "location": 2, + "content": "这种模式下,我们直接将所有数据以 JSON 格式输出,而不是二进制格式。\nUh, where instead of this binary format we output all the data directly as JSON." + }, + { + "from": 939.1882500000006, + "to": 943.1171749999999, + "location": 2, + "content": "从客户端的视角来看,这种模式的数据没那么丰富。\nUh, this is... less rich from a client perspective." + }, + { + "from": 943.1171749999999, + "to": 947.1211749999999, + "location": 2, + "content": "它 ... 如果你想直接从客户端操作数据,\nIt's... if you're trying to operate on the data from the direct client," + }, + { + "from": 947.1211749999999, + "to": 948.7227750000002, + "location": 2, + "content": "这种模式可能不是你想要的。\nit's probably not what you want." + }, + { + "from": 948.7227750000002, + "to": 951.5005499999999, + "location": 2, + "content": "但是,如果你只是想要一个特别简单的结果,\nBut, if you just want a really simple," + }, + { + "from": 951.5005499999999, + "to": 955.2292750000003, + "location": 2, + "content": "如果你只是想执行一个查询然后就从终端返回,\nif you want to make a query and then return it from an endpoint," + }, + { + "from": 955.2547924920009, + "to": 958.8579000000002, + "location": 2, + "content": "而不做任何服务器端的处理,\nuh, without doing any real server side processing," + }, + { + "from": 958.8579000000002, + "to": 961.5105499999999, + "location": 2, + "content": "JSON 模式是一种很不错也很快的方式。\nthe JSON mode is a really nice fast way to do that." + }, + { + "from": 961.5105499999999, + "to": 965.2893249999995, + "location": 2, + "content": "我们会高效地生成 JSON 格式的数据库查询的结果,\nYou... get all the JSON produced efficiently in the database query," + }, + { + "from": 965.2893249999995, + "to": 968.2672999999999, + "location": 2, + "content": "你不需要在应用服务器上做这件事。\nand don't need to do it on your app server." + }, + { + "from": 968.2672999999999, + "to": 971.3453750000006, + "location": 2, + "content": "好的,让我们来大致总结一下。\nUh, so that let us put sort of this all together." + }, + { + "from": 971.3453750000006, + "to": 978.9029249999999, + "location": 2, + "content": "呃,我们有一个关于 Movie 的查询语句,要查 Movie 的名称和上映时间字段,\nUm, with a movie query that.. selects the name and the runtime of a movie," + }, + { + "from": 978.9029249999999, + "to": 981.5055250000003, + "location": 2, + "content": "呃,还有导演和导演的名字。\nand uh the cast and their names." + }, + { + "from": 981.5055250000003, + "to": 985.5846000000009, + "location": 2, + "content": "然后,右边那个大概就是我们要的查询语句。\nAnd uh, approximately the query on the right is what we get." + }, + { + "from": 985.5846000000009, + "to": 988.9880000000006, + "location": 2, + "content": "为了适配幻灯片,\nI... I cleaned it up a little bit, to fit on the slide," + }, + { + "from": 988.9880000000006, + "to": 991.0650749999995, + "location": 2, + "content": "我稍微整理了一下一些比较粗糙的地方。\num, some of the rough edges." + }, + { + "from": 991.0650749999995, + "to": 994.093100000001, + "location": 2, + "content": "但是,好的一方面是即使我们生成了一些粗糙的代码,\nBut, the nice thing is even if we generate code with rough edges," + }, + { + "from": 994.093100000001, + "to": 1000.0490499999999, + "location": 2, + "conte nt": "Postgres is very good at removing... um things like an extra select around things or.", + "content": "Postgres 很擅长于清除多余的内容,比如额外的 select 或其它东西。\nPostgres is very good at removing... um things like an extra select around things or." + }, + { + "from": 1000.0490499999999, + "to": 1002.8268249999995, + "location": 2, + "content": "但大体上来说右边的重点在下面。\nUh, but basically here the idea right is down at the bottom." + }, + { + "from": 1002.8268249999995, + "to": 1006.6556500000003, + "location": 2, + "content": "你可以看到我们在从 Movie 表中查询数据,\nYou can see us uh... selecting from a Movie table," + }, + { + "from": 1006.6556500000003, + "to": 1011.5105000000005, + "location": 2, + "content": "然后我们返回一个元组,其中包含所有我们想要的字段。\nand then our... we're returning a tuple containing all the fields we want," + }, + { + "from": 1011.5105000000005, + "to": 1013.8378249999995, + "location": 2, + "content": "在一个子查询中,\nincluding in the subquery," + }, + { + "from": 1013.8378249999995, + "to": 1020.369350000001, + "location": 2, + "content": "我们对通过链接查到的导演做 array_agg,\nwe're doing an array_agg of uh... all we select from a link to the cast," + }, + { + "from": 1020.369350000001, + "to": 1025.07405, + "location": 2, + "content": "连接到 Person 表,把所有这些建立起来。\njoined against persons, and build all that up." + }, + { + "from": 1025.07405, + "to": 1028.377350000001, + "location": 2, + "content": "好的,现在我们已经讨论过了我们是如何使用 Postgres 的。\nAll right. So now that I've talked about how Postgres." + }, + { + "from": 1028.377350000001, + "to": 1030.1541250000005, + "location": 2, + "content": "接下来我会讨论一下为什么是 Postgres。\nI'm gonna talk about why Postgres." + }, + { + "from": 1030.1541250000005, + "to": 1034.3332999999998, + "location": 2, + "content": "呃,首先 ... 重要的一点是我们的目标是 PostgreSQL,\nUh first... an important thing is we target PostgreSQL," + }, + { + "from": 1034.3332999999998, + "to": 1037.1110749999996, + "location": 2, + "content": "而不是 SQL。\nwe don't target uh \"SQL\"." + }, + { + "from": 1037.1110749999996, + "to": 1038.212175, + "location": 2, + "content": "关于这个,有几点原因。\nAnd there are a few reasons for that." + }, + { + "from": 1038.212175, + "to": 1041.4404000000002, + "location": 2, + "content": "第一,Postgres 是一个十分强大的查询引擎。\nOne, Postgres is an extremely powerful query engine." + }, + { + "from": 1041.4404000000002, + "to": 1046.7206749999998, + "location": 2, + "content": "呃,它可以很出色地运行你发给它的复杂查询语句。\nUh you can give it complex queries and it... does a great job uh... running them." + }, + { + "from": 1046.7206749999998, + "to": 1049.8487999999998, + "location": 2, + "content": "它有一些很重要的功能,比如说 lateral join,\nUh it has important features like lateral join," + }, + { + "from": 1049.8487999999998, + "to": 1052.976925, + "location": 2, + "content": "它很早就有这些功能了,并且做了很好的优化。\nand it has them for a long time and well optimized." + }, + { + "from": 1052.976925, + "to": 1057.9819249999998, + "location": 2, + "content": "嗯,总之,以一个引擎为目标,我们可以更专注。\nUm, and in general, targeting one engine allows us to be much better focused." + }, + { + "from": 1057.9819249999998, + "to": 1061.21015, + "location": 2, + "content": "呃,我们可以利用我们支持的这个数据库,\nUh, we can take advantage of the one database we're supporting," + }, + { + "from": 1061.21015, + "to": 1064.5885250000001, + "location": 2, + "content": "设计一些把它的能力发挥到极致的东西。\nand design something that really pushes it to its limits." + }, + { + "from": 1064.5885250000001, + "to": 1065.9899249999999, + "location": 2, + "content": "而不是分散注意力,\nInstead of being spread thin, " + }, + { + "from": 1065.9899249999999, + "to": 1068.9929249999998, + "location": 2, + "content": "目标放在一堆不同数据库的交集上。\ntrying to target the intersection of a bunch of different databases." + }, + { + "from": 1068.9929249999998, + "to": 1072.9468750000005, + "location": 2, + "content": "但是,即便是这样,我们也在 Postgres 上下了相当多的功夫。\nBut even with all that, we push Postgres pretty hard." + }, + { + "from": 1072.9468750000005, + "to": 1076.3002250000009, + "location": 2, + "content": "呃,举个例子,数组是一个在 Postgres 中表现很棒功能,\nUh, so one example of a thing that works great in Postgres, " + }, + { + "from": 1076.3002250000009, + "to": 1078.6025250000002, + "location": 2, + "content": "但在其它数据库中未必如此。\nbut maybe wouldn't in other places, is arrays." + }, + { + "from": 1078.6025250000002, + "to": 1080.5795000000005, + "location": 2, + "content": "我们有大量的使用数组,\nWhich we make a very heavy use of" + }, + { + "from": 1080.5795000000005, + "to": 1085.1590749999996, + "location": 2, + "content": "包括在部分二进制输出格式中,以及一些内部代码中,\n both as part of our binary output format and uh sometimes internally." + }, + { + "from": 1085.1590749999996, + "to": 1087.5364499999994, + "location": 2, + "content": "呃,但是数组不是一个通用功能。\nUh, but arrays are not a universal feature." + }, + { + "from": 1087.5364499999994, + "to": 1092.6915999999992, + "location": 2, + "content": "呃,MySQL,从几年前开始算是有数组了。\nUh, MySQL... sort of has arrays, as of a couple years ago." + }, + { + "from": 1092.6915999999992, + "to": 1097.8467500000006, + "location": 2, + "content": "但仅支持在 JSON 中使用,一个 JSON 值可以是一个 JSON 数组。\nBut, only in that it has JSON, and a JSON value could be an array of JSON." + }, + { + "from": 1097.8467500000006, + "to": 1103.1770749999994, + "location": 2, + "content": "你无法获得一个整型数组或元组数组。\nBut there's no way to have an array of integers or an array of tuples." + }, + { + "from": 1103.1770749999994, + "to": 1109.4833750000005, + "location": 2, + "content": "呃,说到元组,EdgeDB 支持特定的元组类型。\nUh, and speaking of tuples, EdgeDB supports ad-hoc tuple types." + }, + { + "from": 1109.4833750000005, + "to": 1114.2631500000002, + "location": 2, + "content": "这方面 SQL 就支持的很烂。\nWhich SQL actually has very support... very poor support for operating on." + }, + { + "from": 1114.2631500000002, + "to": 1117.2661500000002, + "location": 2, + "content": "呃,Postgres 表现的还凑活。\nUh, Postgres is just good enough. " + }, + { + "from": 1117.2661500000002, + "to": 1119.1430250000003, + "location": 2, + "content": "很多其它的数据库,我认为,\nAnd a lot of other databases, I think," + }, + { + "from": 1119.1430250000003, + "to": 1124.1480250000002, + "location": 2, + "content": "则无法做到类似从元组中取出一个元素这样的事情。\naren't to do things like access an element from a... tuple." + }, + { + "from": 1124.1480250000002, + "to": 1128.6525250000002, + "location": 2, + "content": "呃,但即使是在 Postgres 中,也需要做一些愚蠢的操作。\nUh, but even in Postgres it requires some kind of silly things." + }, + { + "from": 1128.6525250000002, + "to": 1132.656525, + "location": 2, + "content": "呃,为了操作一个元组,我们实际上把数据打包进了一个数组里,\nUh, to project out of a tuple, we actually pack it into an array," + }, + { + "from": 1132.656525, + "to": 1137.3862500000005, + "location": 2, + "content": "然后我们 unnest 这个数组,并从列定义表里读取元素。\nthen we unnest that array and read the element out of the column definition list." + }, + { + "from": 1137.3862500000005, + "to": 1142.19105, + "location": 2, + "content": "呃,虽然 ... 我觉得 Elvis 还需要提交一个补丁,这部分才能开始工作。\nUh, though... I think Elvis actually had to submit a patch upstream to get that to work." + }, + { + "from": 1142.19105, + "to": 1146.1450000000007, + "location": 2, + "content": "呃,Postgres 还支持事务型的 DDL。\nUh, Postgres also has transactional DDL." + }, + { + "from": 1146.1450000000007, + "to": 1151.1500000000005, + "location": 2, + "content": "呃,也就是说,对 schema 的更改可以在事务中进行。\nUm, so... changes to the schema can be made inside transactions." + }, + { + "from": 1151.1500000000005, + "to": 1155.6795250000002, + "location": 2, + "content": "然后,当然,因为是在事务中进行,如果需要的话,就可以回滚。\nThen of course since they're in transactions, they can be rolled back if they needed to be." + }, + { + "from": 1155.6795250000002, + "to": 1157.9818249999994, + "location": 2, + "content": "这一点很重要,有几个原因。\nThis is really important for a couple of reasons." + }, + { + "from": 1157.9818249999994, + "to": 1160.1590000000006, + "location": 2, + "content": "第一,迁移步骤有可能失败。\nOne, a migration step could fail." + }, + { + "from": 1160.1590000000006, + "to": 1166.1149499999995, + "location": 2, + "content": "呃,可能有一些类型转换 ... 或者检查会失败,\nUh, you could have some... cast in there or some... some check that fails," + }, + { + "from": 1166.1149499999995, + "to": 1169.2931250000006, + "location": 2, + "content": "这时候需要放弃迁移并回滚。\nand you need to give up and roll back." + }, + { + "from": 1169.2931250000006, + "to": 1174.2230499999998, + "location": 2, + "content": "呃,基于非事务型的 DDL,我们也有可能自己实现这些,\nUh, and it might be possible to implement it ourselves on top of non-transactional DDL," + }, + { + "from": 1174.2230499999998, + "to": 1178.151975000001, + "location": 2, + "content": "但,真的,你不会想做的。\nbut... really wouldn't want to." + }, + { + "from": 1178.151975000001, + "to": 1183.2821000000008, + "location": 2, + "content": "但是,更重要的是,完成了一部分的数据库迁移不应该对用户可见。\nBut more importantly than that, partially completed migrations uh shouldn't be visible." + }, + { + "from": 1183.2821000000008, + "to": 1188.7625749999995, + "location": 2, + "content": "呃,数据库在进行迁移这件事,不应该让用户明显地察觉到,\nUh, if there's a migration in progress that shouldn't be apparent to any client," + }, + { + "from": 1188.7625749999995, + "to": 1194.8186250000006, + "location": 2, + "content": "因为这时候数据库处于一种不合理的状态。\nbecause the... the database just isn't in... um a reasonable state." + }, + { + "from": 1194.8186250000006, + "to": 1198.547350000001, + "location": 2, + "content": "不幸的是,除了 Postgres 之外,\nUnfortunately, lots of other databases, other than Postgres, " + }, + { + "from": 1198.547350000001, + "to": 1200.1489499999996, + "location": 2, + "content": "很多其它的数据库都不支持事务型 DDL。\ndon't support transactional DDL." + }, + { + "from": 1200.1489499999996, + "to": 1201.675475000001, + "location": 2, + "content": "这会让我们很难提供令人满意的数据库迁移体验。\nWhich would make it really hard for us" + }, + { + "from": 1201.675475000001, + "to": 1205.4792750000001, + "location": 2, + "content": "这会让我们很难提供令人满意的数据库迁移体验。\nto provide the experience we want for migrations." + }, + { + "from": 1205.4792750000001, + "to": 1208.307100000001, + "location": 2, + "content": "好的,但是与那些不同,Postgres 挺好。\nWell, but separate from that, Postgres is just good." + }, + { + "from": 1208.307100000001, + "to": 1210.9597500000004, + "location": 2, + "content": "它是个不错的查询引擎,也个不错的数据库。\nIt's a good query engine, it's a good database." + }, + { + "from": 1210.9597500000004, + "to": 1216.5403249999995, + "location": 2, + "content": "呃,我们最大的不满是我们认为 SQL 不够现代化。\nUh, the biggest complaint we have about it is that we don't think SQL is... " + }, + { + "from": 1216.5403249999995, + "to": 1220.0938750000005, + "location": 2, + "content": "呃,我们最大的不满是我们认为 SQL 不够现代化。\nuh really stands up in a modern light anymore." + }, + { + "from": 1220.0938750000005, + "to": 1224.4982750000001, + "location": 2, + "content": "但是 ...,但是 Postgres 还是很棒的。\nBut... uh, but Postgres still does a great job." + }, + { + "from": 1224.4982750000001, + "to": 1226.575350000001, + "location": 2, + "content": "我们认为,除了别的那些之外,\nAnd we think that, among other things, " + }, + { + "from": 1226.575350000001, + "to": 1230.5793500000009, + "location": 2, + "content": "EdgeDB 真的有帮助 Postgres 大放异彩。\nEdgeDB actually really helps Postgres shine through." + } + ] +} \ No newline at end of file