def _compile_dispatch_ql(self, ctx: CompileContext, ql: qlast.Base): if isinstance(ql, (qlast.Database, qlast.Delta)): if not (ctx.state.capability & enums.Capability.DDL): raise errors.ProtocolError( f'cannot execute DDL commands for the current connection') return self._compile_ql_migration(ctx, ql) elif isinstance(ql, qlast.DDL): if not (ctx.state.capability & enums.Capability.DDL): raise errors.ProtocolError( f'cannot execute DDL commands for the current connection') return self._compile_ql_ddl(ctx, ql) elif isinstance(ql, qlast.Transaction): if not (ctx.state.capability & enums.Capability.TRANSACTION): raise errors.ProtocolError( f'cannot execute transaction control commands ' f'for the current connection') return self._compile_ql_transaction(ctx, ql) elif isinstance(ql, (qlast.BaseSessionSet, qlast.BaseSessionReset)): if not (ctx.state.capability & enums.Capability.SESSION): raise errors.ProtocolError( f'cannot execute session control commands ' f'for the current connection') return self._compile_ql_sess_state(ctx, ql) elif isinstance(ql, qlast.ConfigOp): if not (ctx.state.capability & enums.Capability.SESSION): raise errors.ProtocolError( f'cannot execute session control commands ' f'for the current connection') return self._compile_ql_config_op(ctx, ql) else: if not (ctx.state.capability & enums.Capability.QUERY): raise errors.ProtocolError( f'cannot execute query/DML commands ' f'for the current connection') return self._compile_ql_query(ctx, ql)
def _compile(self, *, ctx: CompileContext, eql: bytes) -> typing.List[dbstate.QueryUnit]: # When True it means that we're compiling for "connection.fetchall()". # That means that the returned QueryUnit has to have the in/out codec # information, correctly inferred "singleton_result" field etc. single_stmt_mode = ctx.stmt_mode is enums.CompileStatementMode.SINGLE default_cardinality = enums.ResultCardinality.NOT_APPLICABLE eql = eql.decode() statements = edgeql.parse_block(eql) statements_len = len(statements) if ctx.stmt_mode is enums.CompileStatementMode.SKIP_FIRST: statements = statements[1:] if not statements: # pragma: no cover # Shouldn't ever happen as the server tracks the number # of statements (via the "try_compile_rollback()" method) # before using SKIP_FIRST. raise errors.ProtocolError( f'no statements to compile in SKIP_FIRST mode') elif single_stmt_mode and statements_len != 1: raise errors.ProtocolError( f'expected one statement, got {statements_len}') if not len(statements): # pragma: no cover raise errors.ProtocolError('nothing to compile') units = [] unit = None for stmt in statements: comp: dbstate.BaseQuery = self._compile_dispatch_ql(ctx, stmt) if unit is not None: if (isinstance(comp, dbstate.TxControlQuery) and comp.single_unit): units.append(unit) unit = None if unit is None: unit = dbstate.QueryUnit(dbver=ctx.state.dbver, sql=(), status=status.get_status(stmt), cardinality=default_cardinality) else: unit.status = status.get_status(stmt) if isinstance(comp, dbstate.Query): if single_stmt_mode: unit.sql = comp.sql unit.sql_hash = comp.sql_hash unit.out_type_data = comp.out_type_data unit.out_type_id = comp.out_type_id unit.in_type_data = comp.in_type_data unit.in_type_args = comp.in_type_args unit.in_type_id = comp.in_type_id unit.in_array_backend_tids = comp.in_array_backend_tids unit.cacheable = True unit.cardinality = comp.cardinality else: unit.sql += comp.sql elif isinstance(comp, dbstate.SimpleQuery): assert not single_stmt_mode unit.sql += comp.sql elif isinstance(comp, dbstate.DDLQuery): unit.sql += comp.sql unit.has_ddl = True unit.new_types = comp.new_types elif isinstance(comp, dbstate.TxControlQuery): unit.sql += comp.sql unit.cacheable = comp.cacheable if comp.modaliases is not None: unit.modaliases = comp.modaliases if comp.action == dbstate.TxAction.START: if unit.tx_id is not None: raise errors.InternalServerError( 'already in transaction') unit.tx_id = ctx.state.current_tx().id elif comp.action == dbstate.TxAction.COMMIT: unit.tx_commit = True elif comp.action == dbstate.TxAction.ROLLBACK: unit.tx_rollback = True elif comp.action is dbstate.TxAction.ROLLBACK_TO_SAVEPOINT: unit.tx_savepoint_rollback = True if comp.single_unit: units.append(unit) unit = None elif isinstance(comp, dbstate.SessionStateQuery): unit.sql += comp.sql if comp.is_system_setting: if (not ctx.state.current_tx().is_implicit() or statements_len > 1): raise errors.QueryError( 'CONFIGURE SYSTEM cannot be executed in a ' 'transaction block') unit.system_config = True if comp.is_backend_setting: unit.backend_config = True if comp.requires_restart: unit.config_requires_restart = True if ctx.state.current_tx().is_implicit(): unit.modaliases = ctx.state.current_tx().get_modaliases() if comp.config_op is not None: if unit.config_ops is None: unit.config_ops = [] unit.config_ops.append(comp.config_op) unit.has_set = True else: # pragma: no cover raise errors.InternalServerError('unknown compile state') if unit is not None: units.append(unit) if single_stmt_mode: if len(units) != 1: # pragma: no cover raise errors.InternalServerError( f'expected 1 compiled unit; got {len(units)}') for unit in units: # pragma: no cover # Sanity checks na_cardinality = (unit.cardinality is enums.ResultCardinality.NOT_APPLICABLE) if unit.cacheable and (unit.config_ops or unit.modaliases): raise errors.InternalServerError( f'QueryUnit {unit!r} is cacheable but has config/aliases') if not unit.sql: raise errors.InternalServerError( f'QueryUnit {unit!r} has no SQL commands in it') if not na_cardinality and ( len(unit.sql) > 1 or unit.tx_commit or unit.tx_rollback or unit.tx_savepoint_rollback or unit.out_type_id is sertypes.NULL_TYPE_ID or unit.system_config or unit.config_ops or unit.modaliases or unit.has_set or unit.has_ddl or not unit.sql_hash): raise errors.InternalServerError( f'unit has invalid "cardinality": {unit!r}') return units
def _compile(self, *, ctx: CompileContext, eql: bytes) -> typing.List[dbstate.QueryUnit]: eql = eql.decode() if ctx.graphql_mode: eql = graphql.translate( ctx.state.current_tx().get_schema(), eql, variables={}) + ';' statements = edgeql.parse_block(eql) if ctx.single_query_mode and len(statements) > 1: raise errors.ProtocolError( f'expected one statement, got {len(statements)}') units = [] unit = None txid = None if not ctx.state.current_tx().is_implicit(): txid = ctx.state.current_tx().id for stmt in statements: comp: dbstate.BaseQuery = self._compile_dispatch_ql(ctx, stmt) if ctx.legacy_mode and unit is not None: units.append(unit) unit = None if unit is None: unit = dbstate.QueryUnit(txid=txid, dbver=ctx.state.dbver) if isinstance(comp, dbstate.Query): if ctx.single_query_mode or ctx.legacy_mode: unit.sql = comp.sql unit.sql_hash = comp.sql_hash unit.out_type_data = comp.out_type_data unit.out_type_id = comp.out_type_id unit.in_type_data = comp.in_type_data unit.in_type_id = comp.in_type_id else: unit.sql += comp.sql elif isinstance(comp, dbstate.SimpleQuery): unit.sql += comp.sql elif isinstance(comp, dbstate.DDLQuery): unit.sql += comp.sql unit.has_ddl = True elif isinstance(comp, dbstate.TxControlQuery): unit.sql += comp.sql if comp.action == dbstate.TxAction.START: unit.starts_tx = True unit.txid = txid = ctx.state.current_tx().id else: if comp.action == dbstate.TxAction.COMMIT: unit.commits_tx = True else: unit.rollbacks_tx = True units.append(unit) unit = None elif isinstance(comp, dbstate.SessionStateQuery): unit.config = ctx.state.current_tx().get_config() unit.modaliases = ctx.state.current_tx().get_modaliases() else: raise RuntimeError('unknown compile state') if unit is not None: units.append(unit) return units